其他
代码难读/难维护或做 PR 评审时:用 Clean…
适用场景:需要清理或评审代码可读性与可维护性(重构超长函数、统一命名、规范注释与错误处理、提升单测质量)时,按 Clean Code 六项纪律逐项排查,先打分再给出具体改进项。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 48/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 55/100 ×0.15
适用场景
代码难读/难维护或做 PR 评审时:用 通用
解决思路
- 先给目标代码按 0-10 打分并报告当前分数,列出达到 10/10 所需的具体改进项(9-10:名字揭示意图、函数小而聚焦、错误处理一致、测试干净全面;7-8:命名轻微模糊或少数长函数、测试缺边界;5-6:命名不清、重复逻辑、错误处理不一致;3-4:多功能长函数、误导性名字、测试差或缺失;1-2:魔法数字、晦涩缩写、无结构无测试)。
- 以读者为中心:每次命名、划函数边界、决定格式时都问这一步是增加清晰度还是增加阅读成本(代码读写比远超 10:1)。
- 【命名】命名要能回答三件事:为什么存在、做什么、怎么用;如果名字需要注释解释,就换名字。
- 【命名】不用编码前缀和类型信息(不做 Hungarian notation);单字母名只留给极小作用域的循环计数器。
- 【命名】类名用名词,方法名用动词。
- 【命名】同一概念只用一个词:不要在同一项目里混用 fetch、retrieve、get。
- 【命名】作用域越长,名字越长越具体。
- 【命名】变量用意图揭示名:elapsedTimeInDays 而不是 d。
- 【命名】布尔值用谓词短语:isActive、hasPermission、canEdit。
- 【命名】函数用「动词+名词」:calculateMonthlyRevenue() 而不是 calc()。
- 【命名】类名用名词指明职责:InvoiceGenerator 而不是 InvoiceManager。
- 【命名】放心重命名:IDE 让改名几乎零成本,不要因为怕麻烦保留坏名字。
- 【函数】把函数做小、只做一件事:目标 4-6 行、0-2 个参数、单一抽象层级。
- 【函数】遵守 Step-Down Rule:代码自上而下阅读,每个函数调用下一层抽象。
- 【函数】控制参数数量:0 个最好,1 个可以,2 个可接受,3 个以上必须有正当理由。
- 【函数】见到 flag 参数就拆分:render(isPrint) 拆成 renderForPrint() 和 renderForScreen()。
- 【函数】遵守命令查询分离:要么改变状态,要么返回值,不要两者都做。
- 【函数】能抽就抽:只要能把一段逻辑抽成有名字的函数,就抽出来(extract till you drop)。
- 【函数】不留隐藏副作用:函数名必须说出全部真相,例如 checkPassword() 里启动了 session,就重命名或拆开。
- 【函数】长函数按步骤抽成命名子函数:validateInput(); transformData(); saveRecord();
- 【注释与格式】能用命名良好的抽取函数表达的,就不要写注释(最好的注释就是名字)。
- 【注释与格式】只保留这几类注释:法律头、TODO、公共 API 文档、真正解释「为什么」的说明。
- 【注释与格式】删掉注释掉的代码和日志式注释——版本控制记得历史。
- 【注释与格式】解释「做什么」的注释一律替换成更好的名字:// check if eligible → isEligible()。
- 【注释与格式】解释「为什么」的注释保留,例如 // RFC 7231 requires this header for proxies。
- 【注释与格式】概念之间留垂直空白,概念内部保持垂直密集,变量声明靠近使用处。
- 【注释与格式】采用报纸排版:高层函数放在文件顶部,细节在下面。
- 【注释与格式】团队格式约定一次定好并自动化:Prettier、Black、gofmt。
- 【错误处理】先写 try-catch,用它界定事务边界,把错误处理与业务逻辑分开。
- 【错误处理】优先用 unchecked 异常;checked 异常违反开闭原则。
- 【错误处理】按调用者的需要而不是失败类型来定义异常类。
- 【错误处理】不要返回 null(改用空集合、Optional 或抛异常),也不要传 null。
- 【错误处理】用 Special Case / Null Object 模式:返回带默认行为的对象,而不是返回 null 让调用方检查。
- 【错误处理】把错误码换成异常:throw new InsufficientFundsException(balance, amount)。
- 【错误处理】用适配器包装第三方 API(如 PortfolioService 包住厂商 API 并翻译其异常)。
- 【错误处理】异常信息带上操作与状态上下文,如:Failed to save invoice #1234 for customer 'Acme'。
- 【单元测试】按 TDD 三法则:先写失败测试;只写刚好失败的测试;只写刚好通过的生产代码。
- 【单元测试】一个测试只验证一个概念(一个逻辑断言,不一定是单个 assert)。
- 【单元测试】满足 F.I.R.S.T.:Fast、Independent、Repeatable、Self-validating、Timely。
- 【单元测试】建立领域特定的测试语言:写读起来像 DSL 的 helper。
- 【单元测试】像重构生产代码一样随时重构测试代码;脏测试比没有测试更糟。
- 【单元测试】测试结构用 Arrange-Act-Assert,三段清晰分开。
- 【单元测试】测试命名用「场景+预期行为」:shouldRejectExpiredToken 而不是 test1。
- 【单元测试】共享 setup 用 builder/factory helper:aUser().withRole(ADMIN).build()。
- 【单元测试】消除外部依赖让测试稳定:mock 时间、网络、文件系统。
- 【坏味道】识别函数坏味道:参数过多、输出参数、flag 参数、死函数。
- 【坏味道】识别通用坏味道:重复、错误的抽象层级、feature envy、魔法数字。
- 【坏味道】识别测试坏味道:覆盖不足、被跳过的测试、未覆盖的边界条件与失败路径。
- 【坏味道】小步、有测试地重构;绝不边重构边加新功能。
- 【坏味道】践行童子军规则:离开时让代码比来时更干净。
- 【坏味道】重复逻辑抽成共享函数:公共校验 → validateEmail() helper。
- 【坏味道】feature envy 就把方法移到数据所属的类:order.calculateTotal() 而不是 calculator.total(order)。
- 【坏味道】死代码直接删除:无用函数、不可达分支。
- 【坏味道】魔法数字换成命名常量:MAX_LOGIN_ATTEMPTS = 5 而不是裸 5。
- 【坏味道】散弹式修改(shotgun surgery)就把相关变更聚到一个模块里。
- 【常见错误修正】不要缩写名字——写全描述性名字,靠 IDE 自动补全。
- 【常见错误修正】不要写「聪明」的一行代码——展开成可读的命名步骤。
- 【常见错误修正】不要用注释代替重构——抽出命名良好的函数。
- 【常见错误修正】不要捕获泛型异常——捕获具体异常,其余让它向上传播。
- 【常见错误修正】为每条分支、边界和失败模式补测试,不要只测 happy path。
- 【常见错误修正】先写干净代码,只优化测量出的瓶颈,不做过早优化。
- 【常见错误修正】打散上帝类:按单一职责把什么都做的巨类拆开。
- 【常见错误修正】重构前先写 characterization tests 作安全网。
- 【常见错误修正】统一编码约定并用 linter/formatter 强制,避免每个文件像不同代码库。
- 【自检】能否不读函数体就理解每个函数?否则重命名到能说明它做什么。
- 【自检】所有函数是否都在 20 行以内?否则把子操作抽成命名 helper。
- 【自检】是否零注释掉的代码块?否则删除(版本控制有历史)。
- 【自检】错误处理是否与业务逻辑分离?否则抽出 handler,用异常替代返回码。
- 【自检】每个类是否单一职责?否则拆成聚焦且命名良好的类。
- 【自检】每个 public 方法是否都有测试?否则继续改动前先补测试。
- 【自检】测试名是否描述了行为?否则改成 shouldDoXWhenY。
- 【自检】重复是否少于 3 次?否则抽取共享逻辑。
- 【自检】魔法数字是否都有命名常量?否则给常量命名。
- 【自检】所有测试是否在 10 秒内跑完?否则 mock 外部依赖,把集成测试拆出去。
适用边界 · 注意事项
- 一次性原型、spike 或验证性脚本不必先追求整洁度;但要明确区分「用完即弃的试验代码」与要进生产库的代码。
- 没有测试安全网的遗留代码不要直接大规模重构——先补 characterization tests 再动手。
- 性能敏感的热点路径不要为可读性做未经测量的取舍——先测量瓶颈,再决定是否牺牲可读性。