SuperAPI AI Directory

verify-quality-simplify

Testing & quality

View repository

Updated 10-16 09:30

About this skill

Simplify — Systematic code simplification

Entry/Exit

  • Entry: Compilable and testable code
  • Exit: Behavior-preserving simplified version + test verification
  • Points to: After completion, return to verify-workflow-review or continue build
  • Preloading: CANON.md
  • Output path: Simplified code + test verification → verify-workflow-review (continue review) or build-workflow-execute (continue build)

When not to use

  • Behavior is not yet stable, or tests cannot protect the refactoring
  • The current goal is to add features, fix bugs, or satisfy the spec, rather than reduce complexity
  • Simplification requires changing the public API, data structures, or user-visible behavior

Iron Law

Core principles

  • Chesterton's Fence: If you don't understand why a piece of code exists, don't delete it
  • Three similar lines of code > one premature abstraction
  • 500-line rule: A file over 500 lines = simplification candidate
  • Incremental simplification: One change per step, verify each step

Phase 1: Identify simplification targets

Scan the code and mark the following types of simplification targets:

Duplicate code

  • The same logic appears 3+ times
  • Code blocks copied and pasted with only a few parameters changed

Over-abstraction

  • Abstraction layers with fewer than 3 use cases
  • Interfaces or strategy patterns with only 1-2 implementers
  • Indirection layers that bring no reuse benefit

Overly long functions

  • 50-line functions

  • Functions containing 3+ different levels of abstraction

Excessively deep nesting

  • 3 levels of indentation

  • Nested if-else chains eliminated with guard clauses

Dead code

  • Unreferenced exports
  • Unused variables
  • Unreachable branches (if (false), code after throw)

Redundant comments

  • Comments where the code is already self-explanatory
  • Comments that repeat information already expressed by function or variable names

Phase 2: Understand the context

For each target, answer the following questions:

  • Why does it exist? What problem does this code solve? Is it a business requirement, a technical constraint, or historical legacy?
  • Who depends on it? Which modules, tests, or external interfaces depend on it?
  • Has it been changed recently? Actively modified code carries higher risk than stable code.

For each target, further judge:

  • Will deleting/simplifying this affect other modules?
  • Is there test coverage for this code?

For uncertain targets: mark them first, skip them, do not guess.

Phase 3: Incremental simplification

Rule: Change only one target at a time. Run tests after each change; continue only if all green.

Simplification strategies

Target type Strategy Trigger condition
Duplicate code Extract function Only on the 3rd+ occurrence
Over-abstraction Inline the callback usage site, remove the abstraction layer Usage scenarios < 3
Long function Split by responsibility, one responsibility per function > 50 lines
Too deep nesting Use early return (guard clause) to reduce indentation > 3 levels
Dead code After confirming no references, use AskUserQuestion to confirm, then delete No references
Redundant comments Delete; if not self-explanatory enough after deletion, improve naming Code is already self-explanatory

Duplicate code → Extract function (only on the 3rd+ occurrence)

// 第 1 次出现:保持内联
// 第 2 次出现:保持内联,标记 TODO
// 第 3 次出现:提取为函数
function formatDateForDisplay(date: Date): string {
  // 三个地方都用的逻辑现在集中在一处
}

Over-abstraction → Inline + delete

// 前:过度抽象(只有 1 个实现)
interface DataProcessor { process(data: Raw): Result }
class DefaultDataProcessor implements DataProcessor { ... }

// 后:直接使用
function processData(data: Raw): Result { ... }

Long function → Split by responsibility

// 前:一个函数做 3 件事
function handleRequest(req: Request): Response { /* 80 行 */ }

// 后:每个函数一个职责
function validateRequest(req: Request): Validated { ... }
function transformData(data: Validated): Processed { ... }
function buildResponse(data: Processed): Response { ... }

Too deep nesting → guard clause

// 前:4 层嵌套
function process(user: User) {
  if (user) {
    if (user.isActive) {
      if (user.hasPermission) {
        // 真正的逻辑
      }
    }
  }
}

// 后:提前返回,1 层
function process(user: User) {
  if (!user) return;
  if (!user.isActive) return;
  if (!user.hasPermission) return;
  // 真正的逻辑
}

Dead code → Confirm, then delete

# 确认无引用
grep -r "<symbol>" --include="*.ts" --include="*.tsx" --include="*.js"

After confirming no references, use AskUserQuestion to ask: "Should we delete <symbol>? There are no references in the codebase."

Redundant comments → Delete or improve naming

// 前:注释多余
// 设置用户名称
user.setName(name);

// 后 A:删除注释
user.setName(name);

// 后 B:如果删注释后不够清晰,改善命名
user.updateDisplayName(name);

Phase 4: Verification

After simplification is complete, verify in order:

  • All tests pass
  • Lint has no new warnings
  • Behavior unchanged (manual verification of critical paths)
  • Lines of code reduced or unchanged (should not increase)

If any item is not satisfied → roll back the last change and re-evaluate.

Good and bad examples

Good — Reduce abstraction + behavior unchanged

Over-abstraction (a DataProcessor interface with only 1 implementation) → Inline as a processData() function. Fewer lines, shorter call chain, behavior unchanged (full test suite passes). The next modification does not require going through 3-5 layers of indirect calls.

Bad — Over-engineered premature abstraction

When 3 similar lines of code appear, create an abstract factory + strategy pattern. Usage scenarios < 3 but the abstraction layer exists permanently; every modification requires tracing multiple layers of indirect calls, and the comprehension burden actually increases. YAGNI is violated.

Output template

Simplification record (can be merged into the review report or used as a standalone record):

# Simplify 记录: <feature-name>

## 简化目标清单
| 目标 | 类型 | 简化策略 | 原始行数 | 简化后行数 | 状态 |
|------|------|---------|---------|----------|------|
| <symbol-1> | 重复代码 | 提取函数 | X | Y | 完成 |
| <symbol-2> | 过度抽象 | 内联+删除 | X | Y | 完成 |
| <symbol-3> | 死代码 | 确认后删除 | X | 0 | 完成 |

## 验证结果
- 全部测试: PASS (列出命令和结果)
- Lint: 无新警告
- 行为验证: 关键路径手动确认无变化
- 行数变化: 总减少 N 行

## 被跳过的目标
| 目标 | 跳过原因 |
|------|---------|
| <symbol-4> | 不确定依赖关系,需要进一步调查 |

## 结论
- 简化完成 / 需继续(如有被跳过的目标)

Verification evidence

The output or record must include:

  • Input/source: The spec, plan, code, feedback, or release context that was read.
  • Actions performed: The checks, generation, fixes, exports, or release steps actually completed.
  • Verification results: Commands, review conclusions, artifact paths, screenshots, or manual confirmation.
  • Blockers/rollback: Failed items, rollback paths, or issues that require a human partner's decision.

Common excuses

Excuse Reality Consequence
"This is more elegant" Elegance is not the goal; simplicity is. Readability and maintainability matter more than cleverness. Next time it needs modification, no one dares touch "elegant" code—they cannot understand it and are afraid of breaking it. After 3 months, even the author cannot understand it.
"Refactor first, then talk" Acting without understanding = creating bugs. First understand the context in Phase 2. The probability of introducing bugs by refactoring without understanding is > 50% (industry experience). Each introduced bug requires another round of debugging, so the total time > understanding first, then acting.
"Will be used later" YAGNI. Wait until the third use case appears before abstracting. 80% of abstractions built in advance will never have a second use case, but every user has to understand its existence and constraints. Cognitive burden increases permanently.
"This code is too ugly" Ugly but correct > pretty but buggy. First understand why it is ugly. Code changed for the sake of "beautification" introduces regressions, and the hidden constraints behind the ugly code are not understood. Time spent fixing new bugs > the cost of tolerating ugly code.
"Abstracting it makes it cleaner" Abstraction has a cost. Abstractions with < 3 use cases increase the burden of understanding. Premature abstraction makes subsequent modifications require tracing through 3-5 layers of indirect calls, and during the next refactor no one dares to delete this code.

Violating the letter of the rule is violating the spirit. There is no gray area.

Validation failure handling

Failure scenario Handling method
Tests fail after simplification Immediately revert the last change, re-evaluate the simplification strategy, and do not continue changing other goals
Behavior changes after simplification Revert the change, Iron Law violated — unchanged behavior is a hard gate, behavior change = simplification failure
Ununderstood code is deleted Revert the deletion; according to Chesterton's Fence, first understand and then decide whether to delete
Line count increases after abstraction Revert the abstraction, re-evaluate whether this abstraction is really needed (use cases < 3 = not needed)
Simplification introduces new dependencies Revert the change; simplification should not introduce new dependencies, new dependencies = increasing rather than reducing complexity

Red flags

  • Changing multiple goals at once
  • Continuing to change after tests fail
  • Deleting ununderstood code (Chesterton's Fence)
  • More lines after abstraction (abstraction failure)
  • Simplification introduces new dependencies
  • Skipping Phase 2 and starting directly
  • Code behavior changes after simplification (Iron Law violation)
  • Marking complete without running tests

Verification checklist

  • The context of each goal has been understood (Phase 2 complete)
  • Tests pass after each change
  • Behavior unchanged (Iron Law)
  • Total line count has not increased
  • No new lint warnings