simplifying-code

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Simplifying Code

代码简化

Principles

原则

PrincipleRule
Preserve behaviorOutput must do exactly what the input did -- no silent feature additions or removals. Specifically preserve: async/sync boundaries (do not convert sync to async or reverse), error propagation paths (do not alter strategy), logging/telemetry/guards/retries that encode operational intent, and domain-specific steps (do not collapse into generic helpers that hide intent). One carve-out: a shape that existed only in an earlier iteration of the current unshipped scope is not protected behavior. Verify it has no deployed, persisted, public, external, dependent-branch, or in-repo caller outside the resolved scope, and that every required caller update fits inside the edit boundary; otherwise keep the compatibility path. This narrow exemption is about shapes with provably zero consumers -- it never licenses removing a guard, which is governed by the evidence bar under AI Slop Removal
Explicit over cleverPrefer explicit variables over nested expressions. Readable beats compact
Simplicity over cleanlinessPrefer straightforward code over pattern-heavy "clean" code. Three similar lines beat a premature abstraction
Surgical changesTouch only what needs simplifying. Match existing style, naming conventions, and formatting of the surrounding code
Surface assumptionsBefore changing a block, identify what imports it, what it imports, and what tests cover it. Edit dependents in the same pass
Changing an interface, exported name, persisted format, or path reaches past the import graph. Enumerate the producers, consumers, schemas, fixtures, generators, manifests, scripts and CI recipes, config references, and documents that carry the old identifier, and migrate them in the same pass. Close out by searching for the old identifier: zero hits, or one line accounting for each intentional remainder. Renames rot in the fixture holding the old key and the
.env.example
entry, neither of which any import graph contains. When the identifier is a public or exported API, Stop Conditions applies first -- confirm with the user, then enumerate; the sweep runs unprompted only for internal identifiers.
原则规则
保留行为一致性输出代码必须与输入代码的行为完全一致——不得静默添加或移除功能。需特别保留:异步/同步边界(不得在同步与异步间转换)、错误传播路径(不得改变处理策略)、包含业务意图的日志/遥测/防护/重试逻辑,以及特定领域步骤(不得合并为隐藏意图的通用工具函数)。唯一例外:仅存在于当前未发布版本早期迭代中的代码结构不属于受保护行为。需确认该结构在已解析范围外没有已部署、持久化、公开、外部、依赖分支或仓库内的调用者,且所有必要的调用者更新都在编辑边界内;否则需保留兼容路径。这一窄范围豁免仅适用于可证明无调用者的代码结构——绝不允许移除防护逻辑,此类操作需遵循AI冗余代码移除(AI Slop Removal)下的证据标准
清晰优先于巧妙优先使用显式变量而非嵌套表达式。可读性优于简洁性
简洁优先于“整洁”优先使用直白的代码而非过度模式化的“整洁”代码。三行相似代码优于过早抽象
精准修改仅修改需要简化的部分。匹配周边代码的现有风格、命名规范和格式
明确依赖假设修改代码块前,先确定它被哪些模块导入、它导入了哪些模块,以及哪些测试覆盖它。在同一轮修改中同步更新依赖项
修改接口、导出名称、持久化格式或路径会超出导入范围。需枚举所有包含旧标识符的生产者、消费者、 schema、测试数据、生成器、清单、脚本、CI配置、配置引用和文档,并在同一轮修改中完成迁移。最后搜索旧标识符:结果应为零命中,或每条命中都对应有意保留的内容。重命名操作容易在保存旧键的测试数据和
.env.example
条目里遗留问题,而这些内容不在导入图范围内。若标识符属于公开或导出API,需先遵循停止条件(Stop Conditions)——与用户确认,再进行枚举;仅针对内部标识符时,可自动完成全面检查。

Process

流程

  1. Read first -- understand the full file and its dependents before changing anything. Apply Chesterton's Fence: if you see code that looks unnecessary but don't understand why it's there, check
    git blame
    before removing it. First understand the reason, then decide if the reason still applies.
  2. Identify invariants -- what must stay the same? Public API, return types, side effects, error behavior
  3. Identify targets -- find the highest-impact simplification opportunities. Impact = readability and maintainability; prioritize: control flow -> naming -> duplication -> types (see Smell -> Fix table)
  4. Apply in order -- control flow → naming → duplication → data shaping → types. Structural changes first, cosmetic last
  5. Verify -- confirm no behavior change: tests pass, types check, imports resolve
  6. Pre-submit scope audit -- walk every changed line and ask "does the requested task explicitly require this line?" If no, revert it and list it as a follow-up under Residual Risks. Drive-by edits belong in a separate change, not the current patch. For the pre-edit complement on ambiguous-scope requests ("simplify my project"), see
    ia-verification-before-completion
    's Scope Confirmation gate.
  1. 先理解——在修改任何内容前,先理解整个文件及其依赖项。遵循Chesterton's Fence原则:如果你看到看似多余的代码但不理解其存在原因,请先查看
    git blame
    再移除它。先理解原因,再判断该原因是否仍然适用。
  2. 确定不变项——哪些内容必须保持不变?公开API、返回类型、副作用、错误行为
  3. 确定目标——找到影响最大的简化机会。影响度=可读性和可维护性;优先级:控制流 → 命名 → 重复代码 → 类型(见“问题→修复”表格)
  4. 按顺序执行——控制流 → 命名 → 重复代码 → 数据结构 → 类型。先做结构性修改,最后做 cosmetic 修改
  5. 验证——确认行为未改变:测试通过、类型检查通过、导入解析正常
  6. 提交前范围审核——检查每一行修改,问自己“请求的任务是否明确要求修改这一行?”如果没有,撤销修改并将其列为“残留风险”中的后续任务。顺带修改应放在单独的变更中,而非当前补丁。对于范围模糊的请求(如“简化我的项目”),修改前需参考
    ia-verification-before-completion
    的范围确认流程。

Smell → Fix

问题→修复

SmellFix
Deep nesting (>2 levels)Guard clauses with early returns
Long function (>20 lines)Extract into named functions by responsibility
Too many parameters (>3)Group into an options/config object
Duplicated block (3+ occurrences)Extract shared function. Two copies = leave inline; wait for the third
Magic numbers/stringsNamed constants
Complex conditionalExtract to descriptively-named boolean or function
Boolean-returning
if/else
(each branch returns a literal
True
/
False
)
Collapse to the boolean expression itself:
return a and b
, not a branch per literal
Dense transform chain (3+ chained methods)Break into named intermediates for debuggability
Dead code / unreachable branchesDelete entirely -- no commented-out code
Unnecessary
else
after return
Remove
else
, dedent
问题修复方案
深层嵌套(>2层)使用守卫语句提前返回
长函数(>20行)按职责拆分为命名函数
参数过多(>3个)分组为选项/配置对象
重复代码块(3次及以上出现)提取为共享函数。仅出现2次的话保留内联;等出现第三次再处理
魔法数字/字符串替换为命名常量
复杂条件判断提取为命名布尔变量或函数
返回布尔值的
if/else
(每个分支返回字面量
True
/
False
简化为布尔表达式本身:
return a and b
,而非每个字面量对应一个分支
密集的链式转换(3个及以上链式方法)拆分为命名中间变量,提升可调试性
死代码 / 不可达分支完全删除——不要保留注释掉的代码
return后多余的
else
移除
else
,取消缩进

AI Slop Removal

AI冗余代码移除(AI Slop Removal)

When simplifying AI-generated code, specifically target:
  • Redundant comments that restate the code (
    // increment counter
    above
    counter++
    ) -- delete them
  • Unnecessary defensive checks for conditions that cannot occur in context -- remove the guard. Where the guard, retry, workaround, or flag counters an external hazard (a harness default, an upstream bug, a race, a platform quirk), "cannot occur" needs evidence: demonstrate the hazard's precondition is present and handled. A green suite is not that evidence when the run may never have triggered the hazard at all -- absence of failure and absence of the hazard look identical from the outside. If the precondition cannot be reproduced, keep the code and record the gap. Guards against conditions the type system already excludes need no such proof, provided the type is enforced at that boundary rather than merely declared -- deserialized payloads, unchecked API responses, and anything reached through a cast or assertion do not qualify
  • Gratuitous type casts (
    as any
    ,
    as unknown as T
    ) -- fix the actual type or use a proper generic
  • Over-abstraction (factory for 2 objects, wrapper around a single call, util file with 1 function) -- inline the code
  • Inconsistent style that drifts from the file's existing conventions -- match the file
  • Placeholder stubs (
    // ...
    ,
    // rest of code
    ,
    // similar to above
    ,
    // continue pattern
    ,
    // add more as needed
    ) -- leave unsimplified code as-is rather than replacing it with stubs
  • Redundant error wrapping (
    catch(e) { throw e; }
    ,
    catch(e) { throw new Error(e.message); }
    ) that strips the original stack for no reason -- remove the try/catch entirely and let errors propagate
  • Verbose stdlib reimplementations (hand-rolled loops that replicate
    array_filter
    ,
    Array.from
    ,
    Collection::pluck()
    ,
    itertools
    ) -- replace with the stdlib/framework one-liner, but verify edge-case parity first: empty input, null/None guard, no-match default, zero-value path. The one-liner can silently differ from the loop (an empty-input crash, a missing no-match default, lost ordering) -- a structurally cleaner version that changes behavior on an edge case is not a simplification
  • Hand-maintained guarantees the platform, framework, or a downstream layer already enforces (a manual retry wrapping a client that already retries, a hand-rolled TTL cache the ORM/query layer already provides, manual null-coalescing on a value the contract guarantees non-null) -- name the layer that owns the guarantee and what the code collapses to without it. Remove only when it preserves every output, error, side-effect, and ordering; cite the test or a direct comparison proving equivalence, since "it's already guaranteed" over-fires easily
  • Copy-paste with variation -- before proposing a shared abstraction, check whether the duplicated construct can be eliminated by deriving it from an existing source of truth (a constant, an existing map, a generated value). Consolidate into a helper only when elimination isn't behavior-preserving and the duplication has already cleared the 3-occurrence gate (Smell → Fix); below that, leave it inline per Constraints
简化AI生成的代码时,需针对性处理以下内容:
  • 冗余注释:重复代码内容的注释(如
    counter++
    上方的
    // increment counter
    )——删除此类注释
  • 不必要的防护检查:针对当前上下文不可能出现的条件的防护——移除防护逻辑。若防护、重试、临时解决方案或标志是为了应对外部风险(如测试框架默认行为、上游bug、竞态条件、平台特性),则“不可能出现”需要证据:证明风险的前置条件已存在且已被处理。测试套件通过并不代表没有风险——未触发风险和风险不存在从外部看是一样的。若无法复现前置条件,保留代码并记录该缺口。针对类型系统已排除的条件的防护无需此类证明,但前提是该类型在边界处被强制执行,而非仅声明——反序列化 payload、未检查的API响应、通过类型转换或断言得到的内容不满足此条件
  • 不必要的类型转换
    as any
    as unknown as T
    )——修复实际类型或使用合适的泛型
  • 过度抽象(为2个对象创建的工厂、单一调用的包装器、仅含1个函数的工具文件)——内联代码
  • 不一致的风格:偏离文件现有规范的风格——匹配文件现有风格
  • 占位符存根
    // ...
    // rest of code
    // similar to above
    // continue pattern
    // add more as needed
    )——保留未简化的代码,不要用存根替换
  • 冗余错误包装
    catch(e) { throw e; }
    catch(e) { throw new Error(e.message); }
    ):无理由剥离原始堆栈信息——完全移除try/catch,让错误自然传播
  • 冗余的标准库重实现(手动编写的循环,复制
    array_filter
    Array.from
    Collection::pluck()
    itertools
    的功能)——替换为标准库/框架的单行实现,但需先验证边缘情况的一致性:空输入、null/None防护、无匹配默认值、零值路径。单行实现可能与手动循环存在隐性差异(如空输入崩溃、缺少无匹配默认值、顺序丢失)——在边缘情况改变行为的“更简洁”版本不属于简化
  • 平台、框架或下游层已保证的手动维护逻辑(手动重试包裹已自带重试的客户端、手动实现的TTL缓存而ORM/查询层已提供该功能、对契约保证非空的值手动进行空合并)——说明负责该保证的层级,以及移除该代码后的简化结果。仅当能保留所有输出、错误、副作用和顺序时才移除;需引用测试或直接对比证明等效性,因为“已被保证”很容易误判
  • 带变体的复制粘贴代码——在提出共享抽象前,检查重复结构是否可以通过现有数据源(常量、现有映射、生成值)消除。仅当无法消除且重复次数达到3次(见“问题→修复”)时,才合并为工具函数;低于3次则按约束保留内联

Stop Conditions

停止条件

Stop and ask before proceeding when:
  • Simplification requires changing a public API (function signatures, return types, exports)
  • Behavior parity cannot be verified (no tests exist and behavior is non-obvious)
  • Code is intentionally complex for domain reasons (performance-critical, protocol compliance)
  • Scope implies a redesign rather than a simplification
出现以下情况时,请先询问用户再继续:
  • 简化需要修改公开API(函数签名、返回类型、导出内容)
  • 无法验证行为一致性(无测试且行为不明显)
  • 代码因领域原因故意设计得复杂(性能关键、协议合规)
  • 范围暗示需要重新设计而非简化

Constraints

约束

  • Only simplify what was requested -- do not add features, expand scope, introduce new dependencies, or add speculative configurability or flexibility the request did not ask for
  • Leave unchanged code untouched -- do not add comments, docstrings, or type annotations to lines that were not simplified
  • Do not bundle unrelated cleanups into one patch -- each simplification should be a coherent, reviewable unit
  • Do not introduce framework-wide patterns while simplifying a small local change
  • Do not replace understandable duplication with opaque utility layers -- three similar lines are better than a premature abstraction
  • Keep comments that explain intent, invariants, or non-obvious constraints. Remove comments that restate obvious code behavior.
  • If a simplification would make the code harder to understand, skip it
  • Watch for over-simplification: inlining too aggressively removes names that gave concepts meaning; combining unrelated logic into one function hides distinct responsibilities; removing abstractions that exist for testability breaks the test suite
  • When unsure whether a block is dead code, ask instead of deleting
  • 仅简化请求中指定的内容——不要添加功能、扩大范围、引入新依赖,或添加请求未提及的可配置性/灵活性
  • 未修改的代码保持原样——不要给未简化的代码添加注释、文档字符串或类型注解
  • 不要将无关的清理操作打包到一个补丁中——每个简化操作应是一个连贯、可评审的单元
  • 在简化局部小改动时,不要引入框架级别的模式
  • 不要用晦涩的工具层替代易懂的重复代码——三行相似代码优于过早抽象
  • 保留解释意图、不变项或非明显约束的注释。移除重复代码明显行为的注释
  • 若某简化操作会降低代码可读性,请跳过
  • 注意过度简化:过度内联会移除赋予概念意义的名称;将无关逻辑合并到一个函数会隐藏不同职责;移除为可测试性设计的抽象会破坏测试套件
  • 不确定某代码块是否为死代码时,先询问再删除

Verify

验证

  • Tests pass and types check after changes
  • No behavior change (same inputs produce same outputs)
  • Scope limited to requested files -- no drive-by cleanups
  • Match test scope to the importer count surfaced in step 1 (Surface assumptions). Zero external importers: scoped tests on the changed paths. One or more external importers, or shared/utility code edited: run tests covering each importer. Run the full suite when the test runner has no path-scoping mechanism.
  • 修改后测试通过、类型检查通过
  • 行为无变化(相同输入产生相同输出)
  • 范围限制在请求的文件内——无顺带修改
  • 测试范围与步骤1(明确依赖假设)中发现的调用者数量匹配。零外部调用者:仅针对修改路径运行范围测试。一个或多个外部调用者,或修改了共享/工具代码:运行覆盖每个调用者的测试。若测试 runner 无路径范围机制,则运行完整测试套件。

Orchestrator Mode (When Chained With Other Skills)

编排器模式(与其他技能联动时)

When this skill is invoked by an orchestrator that also runs
ia-code-review
,
ia-writing-tests
, or
ia-verification-before-completion
on the same scope, each sub-skill re-resolving scope independently wastes tokens and risks drift. Avoid this by resolving scope exactly once and passing a canonical block to every sub-skill.
Resolved scope format — the orchestrator builds this once, before dispatching any sub-skill:
undefined
当此技能被编排器调用,且编排器同时对同一范围运行
ia-code-review
ia-writing-tests
ia-verification-before-completion
时,每个子技能独立解析范围会浪费token并导致范围漂移。为避免此问题,需仅解析一次范围,并将标准块传递给每个子技能。
已解析范围格式 —— 编排器在调度任何子技能前,先构建此块:
undefined

Resolved scope

Resolved scope

Files:
  • path/to/file-a.ts
  • path/to/file-b.ts
Commit range: HEAD~3..HEAD (or "uncommitted")
Intent: [one-sentence description pulled from the user request or PR description]
Constraints:
  • Preserve public API
  • No behavior change
  • [other constraints specific to this run]

Every chained sub-skill receives this block verbatim in its prompt and uses it as the source of truth — no re-running `git diff --name-only`, no re-parsing the user request, no independent scope resolution. Sub-skills accept `--no-verify --no-report` flags when chained so verification and reporting happen once at the end of the chain, not per-skill. The last sub-skill in the chain runs verification; the orchestrator trusts that result rather than re-verifying.

This prevents two failure modes: scope drift (sub-skill A simplifies one set of files, sub-skill B reviews a different set) and double work (every sub-skill rediscovers the same facts).
Files:
  • path/to/file-a.ts
  • path/to/file-b.ts
Commit range: HEAD~3..HEAD (or "uncommitted")
Intent: [从用户请求或PR描述中提取的一句话说明]
Constraints:
  • Preserve public API
  • No behavior change
  • [本次运行的其他特定约束]

每个联动的子技能都会在其提示中收到此块的原文,并将其作为唯一可信来源——无需重新运行`git diff --name-only`、重新解析用户请求或独立解析范围。联动时,子技能接受`--no-verify --no-report`标志,以便验证和报告仅在链的末尾执行一次,而非每个技能执行一次。链中的最后一个子技能运行验证;编排器信任该结果,不再重新验证。

这避免了两种失败模式:范围漂移(子技能A简化一组文件,子技能B评审另一组文件)和重复工作(每个子技能重复发现相同事实)。

Integration

集成

  • ia-code-simplicity-reviewer
    agent -- analysis-only pass producing a simplification report (no code changes). Use before refactoring to identify targets.
  • ia-code-simplicity-reviewer
    Agent —— 仅执行分析,生成简化报告(不修改代码)。可在重构前使用,以确定简化目标。

Output

输出

After simplifying, report:
  • Scope touched: files and functions modified
  • Key simplifications: what changed and why (one line each)
  • Verification: tests pass, types check, no behavior change
  • Residual risks: assumptions made, areas not touched that may need attention
简化完成后,需报告:
  • 修改范围:修改的文件和函数
  • 关键简化点:修改内容及原因(每条一行)
  • 验证结果:测试通过、类型检查通过、行为无变化
  • 残留风险:做出的假设、未修改但可能需要关注的区域