# Code Simplification

> Use after implementing features, before claiming a phase is complete, when reviewing AI-generated code, or when code feels overly complex. Also use when you notice repeated patterns across files, a function exceeds 40 lines, nesting exceeds 3 levels, or an abstraction has only one implementation. Covers duplication, dead code, over-engineering, and AI-specific bloat patterns like verbose error handling and redundant type checks. Use when this capability is needed.

- Skill: `tomevault-io/code-simplification-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/code-simplification-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/code-simplification-2/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/code-simplification-2

---


<!-- TOKEN BUDGET: 400 lines / ~1200 tokens -->

# Code Simplification

<activation>

## When This Skill Activates

- After all tasks in a phase are complete (before shipping)
- When reviewing code generated by multiple builder agents
- When a file has been touched by 3+ different tasks
- When you notice patterns repeating across files
- Before claiming a phase is production-ready

**The simplifier agent references this skill for systematic cross-task analysis.**

## Natural Language Triggers
- "simplify this", "clean up", "too complex", "reduce complexity", "this is bloated"

</activation>

## Overview

AI-generated code accumulates complexity. Each task is implemented in isolation by a fresh agent that can't see the full picture. After multiple tasks, duplication creeps in, abstractions multiply, and dead code lingers.

**Core principle:** The simplest code that works correctly is the best code. Complexity is a cost, not a feature.

**This skill applies after implementation, not during.** Don't prematurely optimize -- but don't ship bloat either.

<instructions>

## Simplification Process

When reviewing code for simplification:

1. **Identify scope:** What files changed in this phase? (Use git diff)
2. **Scan for duplication:** Look for similar patterns across files
3. **Check complexity:** Flag functions exceeding thresholds
4. **Find dead code:** Look for unused definitions
5. **Spot over-engineering:** Look for abstractions with single implementations
6. **Check AI patterns:** Apply the AI anti-pattern checklist
7. **Prioritize findings:**
   - **High:** Clear duplication (3+), dead code, obvious bloat
   - **Medium:** Complexity reduction, near-duplicates
   - **Low:** Style consistency, minor simplifications

## Duplication Detection

### What to Look For

**Exact duplicates:** Identical code blocks in different files or functions.
```
# RED FLAG: Same logic in two places
def validate_user_email(email):
    if not email or "@" not in email:
        raise ValueError("Invalid email")

def validate_contact_email(email):
    if not email or "@" not in email:
        raise ValueError("Invalid email")
```

**Near duplicates:** Same structure, different details.
```
# RED FLAG: Parallel structure, only names differ
def create_user(data):
    validate(data)
    user = User(**data)
    db.add(user)
    db.commit()
    return user

def create_project(data):
    validate(data)
    project = Project(**data)
    db.add(project)
    db.commit()
    return project
```

**Parallel hierarchies:** When adding a new type requires changes in multiple places.

**Copy-paste config:** Same configuration blocks repeated in Docker, Terraform, or CI files.

### The Rule of Three

- **2 occurrences:** Note it, but don't extract yet.
- **3 occurrences:** Extract. The pattern is real.
- **1 abstraction serving 1 caller:** Inline it. The abstraction has no value.

## Complexity Reduction

### Techniques

**Extract method:** When a function does too many things.
```
# BEFORE: One function doing everything
def process_order(order):
    # validate (10 lines)
    # calculate totals (15 lines)
    # apply discounts (12 lines)
    # save to database (8 lines)
    # send notification (6 lines)

# AFTER: Clear responsibilities
def process_order(order):
    validate_order(order)
    totals = calculate_totals(order)
    totals = apply_discounts(totals, order.customer)
    save_order(order, totals)
    notify_order_placed(order)
```

**Early returns / guard clauses:** Eliminate deep nesting.
```
# BEFORE: Nested conditionals
def get_discount(user):
    if user:
        if user.is_premium:
            if user.years > 5:
                return 0.20
            else:
                return 0.10
        else:
            return 0.0
    else:
        return 0.0

# AFTER: Guard clauses
def get_discount(user):
    if not user or not user.is_premium:
        return 0.0
    if user.years > 5:
        return 0.20
    return 0.10
```

**Replace conditionals with polymorphism:** When type-checking drives behavior.

**Simplify boolean expressions:** Collapse nested boolean logic.

### Complexity Thresholds

| Metric | Acceptable | Review | Refactor |
|--------|-----------|--------|----------|
| Function length | < 20 lines | 20-40 lines | > 40 lines |
| Nesting depth | <= 2 levels | 3 levels | > 3 levels |
| Parameters | <= 3 | 4-5 | > 5 |
| Cyclomatic complexity | <= 5 | 6-10 | > 10 |

## Dead Code Identification

### What Counts as Dead Code

- **Unused imports** -- imported but never referenced
- **Unused variables** -- assigned but never read
- **Unreachable branches** -- conditions that can never be true
- **Commented-out code** -- if it's needed, it's in git history
- **Unused functions/methods** -- defined but never called
- **Vestigial parameters** -- accepted but never used
- **Feature flags for shipped features** -- the flag is always on

### What Does NOT Count

- Public API surface -- may have external callers
- Test utilities -- called only from tests
- Interface implementations -- required by contract
- Error handlers for rare conditions -- needed for robustness

## Over-Engineering Indicators

### Premature Abstraction

```
# OVER-ENGINEERED: Abstract factory for one implementation
class NotificationFactory:
    @staticmethod
    def create(type):
        if type == "email":
            return EmailNotifier()
        raise ValueError(f"Unknown: {type}")

# SIMPLE: Just use the thing directly
notifier = EmailNotifier()
```

**Rule:** If there's only one implementation, don't create an abstraction. Add it when the second implementation arrives.

### Unnecessary Indirection

```
# OVER-ENGINEERED: Service wrapping a service
class UserService:
    def get_user(self, id):
        return self.repository.get_user(id)  # Just passes through

# SIMPLE: Use the repository directly where needed
user = repository.get_user(id)
```

### Configuration for One Value

```
# OVER-ENGINEERED
MAX_RETRIES = config.get("max_retries", 3)

# SIMPLE (if this is the only place retries happen)
MAX_RETRIES = 3
```

**Rule:** Make it configurable when a second consumer needs a different value, not before.

## AI-Specific Anti-Patterns

AI code generators commonly produce these patterns. Watch for them:

### Verbose Error Handling
```
# AI BLOAT: Every function has identical error handling
def get_user(id):
    try:
        user = db.query(User).get(id)
        if user is None:
            raise ValueError(f"User {id} not found")
        return user
    except ValueError:
        raise
    except Exception as e:
        logger.error(f"Error getting user: {e}")
        raise RuntimeError(f"Failed to get user {id}") from e

# SIMPLER: Let exceptions propagate naturally
def get_user(id):
    user = db.query(User).get(id)
    if user is None:
        raise ValueError(f"User {id} not found")
    return user
```

### Redundant Type Checks
```
# AI BLOAT: Checking types that can't be wrong
def process(items: list[str]) -> None:
    if not isinstance(items, list):
        raise TypeError("Expected list")
    for item in items:
        if not isinstance(item, str):
            raise TypeError("Expected str")
        # actual logic...

# SIMPLER: Trust the type system
def process(items: list[str]) -> None:
    for item in items:
        # actual logic...
```

### Over-Defensive Coding
```
# AI BLOAT: Null checks where nulls can't happen
user = get_authenticated_user()  # Already validated by middleware
if user is not None and user.id is not None:  # Impossible to be None
    process(user)

# SIMPLER: Trust your system boundaries
user = get_authenticated_user()
process(user)
```

### Unnecessary Wrapper Functions
```
# AI BLOAT: Wrapping standard library
def read_json_file(path):
    with open(path) as f:
        return json.load(f)

# Used exactly once -- just inline it
with open(config_path) as f:
    config = json.load(f)
```

### Shell/Bash AI Anti-Patterns

```bash
# AI BLOAT: Excessive logging in every function
my_function() {
  log_info "Entering my_function with args: $*"
  log_debug "Checking if file exists: $1"
  if [ -f "$1" ]; then
    log_info "File found: $1"
    cat "$1"
    log_info "Exiting my_function successfully"
  else
    log_error "File not found: $1"
    return 1
  fi
}

# SIMPLER: Let set -e and meaningful exit codes do the work
my_function() {
  cat "$1"
}
```

```bash
# AI BLOAT: Wrapping simple commands in functions used once
get_current_branch() { git rev-parse --abbrev-ref HEAD; }
get_repo_root() { git rev-parse --show-toplevel; }

# SIMPLER: Inline when used once
branch=$(git rev-parse --abbrev-ref HEAD)
```

```bash
# AI BLOAT: Redundant existence checks
if [ -n "${MY_VAR:-}" ]; then
  if [ "${MY_VAR}" != "" ]; then  # Redundant -- -n already checks this
    process "$MY_VAR"
  fi
fi

# SIMPLER: One check is enough
if [ -n "${MY_VAR:-}" ]; then
  process "$MY_VAR"
fi
```

</instructions>

<examples>

## Example: Full Simplification Finding

<example type="good" title="Identifying and resolving cross-agent duplication">
**Context:** Three builder agents each implemented a task touching API endpoints. Each added its own request validation helper.

**Finding:** Three near-identical `validate_request_body()` functions in `routes/users.py`, `routes/projects.py`, and `routes/teams.py`.

**Before (in each file):**
```python
def validate_request_body(body, required_fields):
    if not body:
        raise HTTPError(400, "Request body required")
    for field in required_fields:
        if field not in body:
            raise HTTPError(400, f"Missing field: {field}")
```

**After (extracted to shared module):**
```python
# validators/request.py
def validate_request_body(body, required_fields):
    if not body:
        raise HTTPError(400, "Request body required")
    for field in required_fields:
        if field not in body:
            raise HTTPError(400, f"Missing field: {field}")

# Each route file now imports:
from validators.request import validate_request_body
```

**Priority:** High -- 3 exact duplicates, Rule of Three applies.
</example>

</examples>

<rules>

## Red Flags -- STOP and Simplify

- Same logic in 3+ places
- Function > 40 lines
- Nesting > 3 levels
- Abstract class with one concrete implementation
- Wrapper that just delegates
- Config for a value used in one place
- Try/except that re-raises the same exception
- Type check for a statically typed parameter
- Null check after a function that can't return null
- Commented-out code blocks

## Common Rationalizations

| Excuse | Reality |
|--------|---------|
| "We might need it later" | YAGNI. Add it when you need it. |
| "It's more extensible" | Extensibility without use cases is waste. |
| "The abstraction makes it cleaner" | One caller = inline is cleaner. |
| "Deleting code feels risky" | Git remembers. Dead code is maintenance cost. |
| "It's just a few extra lines" | Lines compound. 10 files x 5 extra lines = 50 lines of noise. |
| "The AI generated it, it must be right" | AI optimizes for completeness, not simplicity. |
| "Refactoring might break things" | Tests exist. If they don't, add them first. |

</rules>

## Integration

**Referenced by:**
- **shipyard:simplifier** -- Uses this skill as the analysis framework for cross-task simplification
- **shipyard:reviewer** -- Code quality review (Stage 2) checks for complexity

**Pairs with:**
- **shipyard:shipyard-tdd** -- Tests make simplification safe
- **shipyard:shipyard-verification** -- Simplification claims need evidence

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/lgbarn) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-11 -->

