SuperAPI AI Directory

code-simplify

Coding & development

View repository

Updated 10-15 17:25

About this skill

Code Simplification

Simplify code, identify smells, reduce maintenance costs. Scope is functions, classes, filesinternal, and local moves between them—the other side of not sinking down is arch-design(the height of modules, dependencies, layering).

Why it is needed:Code has a hidden cost—every future change requires first understanding it. If it is hard to read, convoluted, and duplicated everywhere, this cost is high, and it compounds. Smells are not a moral issue; they are residue left by rushed iteration. The purpose of simplification is to reduce the cost of "every future change," not to reduce the number of lines. There is only one criterion for judging whether a simplification succeeded:When a new colleague reads the simplified version, does he understand it faster than the original.

It simultaneously opposes two things:being afraid to touch(code rots because no one dares to change it) andover-simplification(compressing code into "clever" formulations that require pausing to parse, or casually refactoring unrelated code and bloating the diff). One red line runs throughout:simplification strictly preserves externally visible behavior—only change how the code expresses itself, not what it does.

Two invocation modes and a confirmation gate

This skill has two entry points, handled differently—the only difference is whether "the user asked for it."

The user actively invokes / explicitly asks for simplification → directly follow the process below. The user has already authorized it.

The agent proactively discovers and invokes it(while you are reading code or changing something else, you run into a smell that clearly affects maintainability) → confirm first, then act. Do not change anything directly. First give the user a brief report:

  • what smell was found, in which file, which section;
  • its maintenance cost—how often this code is changed, whether it has had bugs, how many people read it;
  • how you suggest changing it, with what technique;
  • impact scope—how many files, call sites, and test suites will be touched.

Only after the user nods (or picks a few of them) do you enter the process.Why this gate is needed:Changing code without being asked creates an unexpected diff, interrupts what the user is doing, and mixes a "cleanup" into a "bug fix" commit. Proactively discovering problems is valuable—but discovery ≠ action. Report and wait for confirmation, returning the decision to the user.

Several iron rules

One, strictly preserve externally visible behavior. All inputs, outputs, side effects, error behavior, and edge cases must be exactly the same before and after simplification. If you are unsure whether a simplification will affect behavior, do not do it. A direct test: if the simplification forces you to change tests to pass, you have probably changed behavior—back off.

Two, understand first, then act (Chesterton's fence). When you see convoluted code, first figure out why it was written that way, then decide whether to change it—it may be for performance, platform limitations, or historical reasons. If you cannot understand it, first read enough context and check git blame. If you cannot answer "why is it here," you are not ready to simplify it.

Three, follow project conventions. Simplification is about making code fit the patterns already in the codebase, not about imposing personal preferences. First read CLAUDE.md / AGENTS.md and see how nearby code handles similar situations—import order, naming, error handling, depth of type annotations. A "simplification" that breaks consistency is not simplification, it is creating noise.

4. Touch only the relevant scope. By default, only simplify the code you are currently working on. Do not refactor unrelated code along the way—that dirties the diff and introduces regression risk to places you did not intend to change. If you want to expand the scope, say so explicitly, or use scan mode.

5. Take small steps and verify each one. One technique at a time; after changing, run the tests; once green, move to the next. Do not pile up a bunch of simplifications into one big untested change—if something goes wrong, you will not know which step caused it.Keep simplification commits separate from feature / bug fix commits: refactoring and adding a feature in one PR are two PRs.

6. Clarity beats cleverness. Explicit code beats compact code—as long as the compact version requires pausing to think before you can read it. Fewer lines is not the goal; reading quickly is.

When to use

  • The feature is written and the tests pass, but the implementation is heavier than it should be.
  • During code review, readability or complexity was called out.
  • You run into deep nesting, overly long functions, vague naming.
  • Code written under deadline pressure, to come back and clean up afterward.
  • Duplication or inconsistency was introduced after merging.
  • Doing a periodic bad-smell survey of a directory / module (scan mode).

Do not use for:

  • The code is already clear—do not simplify for the sake of simplifying.
  • You do not yet understand what this code does—understand first (iron rule two).
  • Performance-critical, and the "simpler" version measurably runs slower.
  • The module is about to be entirely rewritten—simplifying code that will be discarded is wasted effort.
  • The change would alter externally visible behavior—that is a new feature or bug fix, not simplification; use spec-design or systematic-debugging。
  • The problem is at the level of module structure, dependency direction, layering—that belongs to arch-design. One-sentence dividing line: what is being changed is a moduleboundary → arch-design; what is being changed is inside a moduleinternals → this skill.

Process

A. 框定        →  B. 理解        →  C. 识别坏味道        →  D. 小步执行        →  E. 验收
   (范围与模式)     (Chesterton)      (查目录、定手法)        (一次一个、每步跑测试)     (整体回看)

A Frame the scope and patterns

  • When the Agent proactively invokes it,pass the confirmation gate first(see above).
  • Define clearly the scope to touch: is it a single-point cleanup of some file / function at hand, or a survey of a directory / module (scan mode, see below).
  • Single-point cleanup: the scope is the code you are currently working on, no expansion.

B Understand (Chesterton's fence)

Before touching any code, be able to answer:

  • What is this code's responsibility? Who calls it? What does it call?
  • What are the boundary cases and error paths?
  • Are there tests that define its expected behavior?
  • Itwhywas it written this way? (Performance? Platform constraints? Historical reasons?) Check git blame.

If you cannot answer, read the context first, do not touch it.

C Identify bad smells

Scan the code, recognizespecific bad smells——Each one is a clear signal, not a vague "I think it's messy." The "Quick Identification Table" below lists the most commonly encountered kinds, enough for you to recognize at a glance.For the complete catalog of bad smells (aligned with the twenty-plus in Refactoring) and the corresponding refactoring techniques, see references/smells-and-refactorings.md——When you identify a bad smell not in the quick table, or are unsure which technique to use, read it.

D Execute in Small Steps

For each simplification to be made:

  1. Make this one change (one technique).
  2. Run the test suite.
  3. Green → commit, or continue to the next one.
  4. Red → revert, rethink.

Large techniques (Extract Class, Replace Conditional with Polymorphism, and the likethat change shape) are themselves a disciplined sequence of small techniques—break them into small steps, and between each step the system remains compilable and testable. For the technique catalog, see references/smells-and-refactorings.md。

500-Line Rule: If a single simplification requires touching more than 500 lines, invest in automation (codemod, scripts, AST transformation), don't hand-edit—at that scale, hand-editing is both error-prone and hard to review.

For before-and-after simplification examples in various languages, see references/language-examples.md。

E Acceptance

When everything is done, step back and look at the whole—go through the "Acceptance Checklist" below. If the "simplified" version is actually harder to read and harder to review,revert. Not every simplification attempt will succeed.

Scan Mode (Directory / Module Survey)

When the task is "scan and optimize a piece of codebase" rather than a single-point cleanup:

  1. Define the scan scope (one directory, one module).
  2. Go through the bad smell catalog file by file, recording: smell type, location, maintenance cost signals (change frequency, bug history, how many places depend on it).
  3. Produce alist sorted by maintenance cost—highest cost first.Do not change anything directly.
  4. Hand the list to the user,the user picks which ones to touch.。
  5. For the selected items, go through B→D one by one.

Scan mode naturally comes with a confirmation gate—producing the list and waiting for the user to pick is itself the confirmation.

Quick Identification Table

The most commonly encountered bad smells, enough to recognize at a glance. For the complete catalog, see references/smells-and-refactorings.md。

Structural Complexity

Bad Smell Signal Simplification Direction
Deep nesting (3+ levels) Control flow is hard to follow Extract guard clauses, or extract into named helper functions
Extra-long function (50+ lines) Multiple responsibilities crammed together Split into small functions with a single responsibility and clear names
Nested ternary Requires mental stack pushing to parse Replace with an if/else chain, switch, or lookup table
Boolean parameter flag doThing(true, false) You can't tell what it means Replace with an options object, or split into multiple functions
Repeated conditional checks The same if scattered in multiple places Extract into a clearly named predicate function

Naming and Readability

Bad Smell Signal Simplification Direction
Generic naming data result tmp val doesn't say what it contains Change to a name that describes the content
Abbreviated naming usr cfg btn Have to guess Use full words, unless it's a common abbreviation (id、url)
Misleading naming Named get but changes state Rename it to reflect its real behavior
"What it does" comments // 计数器加一 duplicate the code Delete the comment—the code itself is clear enough
"Why" comments Carry intent that the code cannot express Keep them

Redundancy

Bad smell Signal Direction for simplification
Duplicated code The same 5+ lines scattered in multiple places Extract into a shared function
Dead code Unreachable branches, unused variables, commented-out blocks Delete (after confirming it's truly dead)
Worthless wrapper Wraps a layer but doesn't save the caller any trouble Inline it and call the underlying layer directly
Over-engineering A factory of factories, a strategy pattern with only one strategy Replace with a direct, simple approach
Speculative generalization An abstraction kept for an imagined requirement Delete it; add it when it's actually needed

From a maintenance-cost perspective: is it worth simplifying

Not every bad smell is worth touching. To judge priority, look at three signals:

  • Change frequency—has this code been changed often recently? (git log) In places changed frequently, you pay the cost of incomprehensibility every time.
  • Bug history—how many bugs have occurred here? Repeated bugs are often caused by bad smells.
  • Number of readers—how many places depend on it? The more readers, the greater the multiplier on the cost of incomprehensibility.

Frequently changed + hard to understand + has had bugs = prioritize simplification. Corners that barely change and no one reads = don't touch—simplifying them yields no return and adds regression risk for nothing. This is what "reduce maintenance cost" means in practice: spend effort where the cost is truly high.

Common self-justifications

Self-justification Reality
"If it runs, don't touch it" Code that runs but is hard to read is also hard to fix when it breaks. Simplify it now, and you save time on every future change.
"Fewer lines means simpler" One nested ternary line is not simpler than five lines of if/else. Simple is about speed of understanding, not line count.
"I simplified that other part along the way too" Unrelated simplification dirties the diff and introduces regressions in places you didn't intend to change. Keep the scope tight.
"The types are self-documenting already" Types describe structure, not intent. A clearly named function explains "why" better than a type signature.
"This abstraction might be useful later" Don't keep speculative abstractions. If it's useless now, it's worthless complexity. Delete it and add it if you really need it.
"The original author must have had a reason" Maybe. Check git blame and use Chesterton's fence. But accumulated complexity often has no reason—it's just leftovers from rushing.
"I refactored it along the way while adding a feature" Keep refactoring and features separate. Mixed changes are hard to review, hard to revert, and hard to understand in history.

Red flags

  • A simplification forces you to change tests to pass—you probably changed behavior.
  • The "simplified" code is longer and harder to follow than before.
  • Renaming according to personal preference rather than project conventions.
  • Deleting error handling to make the code "cleaner".
  • Simplifying code you don't fully understand.
  • Accumulating a pile of simplifications into one large, hard-to-review commit.
  • Refactoring code outside the scope of the current task when not asked to.

Acceptance checklist

  • All existing tests pass without modification.
  • The build succeeds with no new warnings; linters / formatters pass.
  • Each simplification is an independently reviewable incremental change; the diff is clean with no unrelated changes mixed in.
  • The simplified code follows project conventions (compare with CLAUDE.md / AGENTS.md).
  • No error handling was deleted or weakened.
  • No dead code is left behind (unused imports, unreachable branches).
  • [ ] (When the Agent proactively initiates) reported and obtained user confirmation before taking action.
  • A colleague or review agent would agree this change is a net improvement.

Anti-patterns

  • ❌ The Agent proactively spots a bad smell and just fixes it—must report first and wait for confirmation.
  • ❌ Mixing behavior changes into a simplification—a simplification does not change external behavior; if behavior changes, it's a new feature or a bug fix.
  • ❌ Simplifying for the sake of simplifying—if the code is already clear, leave it alone.
  • ❌ Simplifying code you don't understand—understand it first (Chesterton's fence).
  • ❌ Refactoring unrelated code along the way—bloats the diff and introduces regressions.
  • ❌ Accumulating a pile of simplifications into one big commit—can't pinpoint problems when they arise.
  • ❌ Mixing simplification with features / bug fixes in one commit—hard to review, hard to revert.
  • ❌ Using line count as the success metric for simplification—speed of understanding is what matters.
  • ❌ Simplifying code that's about to be rewritten—wasted effort.
  • ❌ Handling module structure / dependencies / layering issues here—that's the level of arch-design .

Relationship with other skills

  • arch-design—structural issues at the level of modules, dependency direction, and layering belong to it; local cleanup within functions, classes, and files, and between them, belongs to this skill. If you're touching module boundaries → arch-design; if you're touching the inside of a module → code-simplify。
  • test-designer—the premise of simplification is that behavior is covered by tests (Iron Rule One). When the safety-net tests are missing and the behavioral surface isn't obvious, it designs characterization tests first, then simplification can begin.
  • systematic-debugging—if the "bad smell" is actually a bug with reproduction steps, that's a defect, not a simplification target; go through it.