FEATURED · 精选文章

代码质量评判指南:七个维度构建可读、可维护的工程实践

发布时间 / 2026/9/10 5:51:19
来源 / 创域科博编辑部
栏目 / 资讯中心
代码质量评判指南:七个维度构建可读、可维护的工程实践 上个月代码评审我面对一段功能完全正确、测试也全部通过的代码硬是坐了二十分钟没有点击通过。那段代码让我想起家里装修时的一个场景开关一拉灯确实亮了但整面墙的线管都在发热。后来我花了三个晚上帮同事把那段代码拆成八个职责清晰的小函数功能没有任何变化但全组人都说终于看懂了。代码质量这个话题我写了十多年程序越写越觉得它从来不是能不能跑的问题而是别人敢不敢改、你敢不敢动的问题。这篇内容就是围绕这个核心展开的。开篇先把结论放在这里代码质量不是一个玄学概念它完全可以被拆解成一组可观察、可测量、可训练的维度。下面我从评判标准到能力养成按一条能落地的路线展开希望能给你一个系统框架不再靠感觉写代码。1. 烂代码不是技术债是利滚利的高利贷很多人把代码能跑当成及格线这完全低估了代码的长期成本。工业界有个广为流传的结论软件生命周期里真正花在写上面的时间只占很小一部分绝大部分时间花在阅读、理解、修改和排错上。你写的每一行代码在未来可能被读上几十次、几百次而写它只需要一次。所以这段代码别人要在什么成本下才能读懂才是真正要关注的核心指标。技术债这个比喻其实不太准确因为真正的债有一个还款计划而烂代码留下的东西更像高利贷——每天都在利滚利。今天为了赶进度绕过一个异常场景明天就得花三倍时间在线上日志里捞问题这个模块少一个抽象层半年后接手的同事就得在所有调用点打上补丁。你会发现一个规律烂代码的维护成本不是线性增长的而是呈指数膨胀的。最初的一个将就后面会滚出十个不得不的妥协。还有一个容易被忽视的点代码质量直接影响团队协作。我给一个比较悲观的观察如果一个组里有一块谁都不敢碰的核心模块那么这个组的所有人实际上都被它绑架了。新来的人不敢碰老人不愿意碰需求来了只能在外围打转用更多的补丁去覆盖旧的裂缝。代码质量差的时候表面上只是代码问题实际上团队的效率、士气、人才留存全都搭进去了。所以判断代码质量首先要建立一个根本性的观念转变代码的第一读者是人其次才是机器。机器只看结果而人要看过程、看意图、看边界。凡是重视运行结果而忽视阅读成本、修改成本和排错成本的做法从长期看都是不划算的。这个观念贯彻到位后面的所有评判维度才立得住。2. 七个维度一套能落地的质量评判框架我这些年参与评审逐渐把代码质量的评判标准收敛到七个维度。每个维度都不是空谈都能对应到具体的判断动作。2.1 可读性代码是给人读的顺便被机器执行可读性是一个经常被挂在嘴边、却很少被认真执行的标准。判断一段代码可读性最直接的方法就是让一个从没看过这段代码的人用三十秒通读一遍然后告诉你这段代码在做什么。如果他只能说出处理数据做了一些操作这类模糊描述可读性就不合格。可读性的敌人往往不是笨而是懒。变量名叫temp、data、flag是最典型的信号函数体超过二十行还在继续膨胀是第二个信号。我见过最经典的例子是一个五百行的函数里面每三行就出现一个if分支所有分支都在用magic_number做判断没有注释没有子函数。这种代码不是给机器写的是故意写给后人上刑的。可读性有几个具体抓手命名要能表达意图isOrderPaid就比flag1清楚函数要做单件事超过一个职责就要拆注释应该解释为什么而存在比如业务背景和特殊约束而不是解释做了什么——后者看代码就能看出来。用一句我常挂在嘴边的话如果一段代码需要大量注释才能自圆其说那你更应该去改代码而不是补注释。2.2 可维护性改一处会不会牵一发动全身可维护性衡量的核心是变更成本。需求变更是软件开发的常态一段代码接不接受变化直接决定了它值不值得被保留。判断可维护性有两个朴素的方法第一把一段逻辑的所有调用方列出来如果改它需要同步修改超过三处警惕耦合第二问一句如果业务规则变了我要改几个文件——理想答案是一个且是唯一的、独立的一个。耦合度是这里的关键。模块之间一旦通过隐性约定相互依赖比如 A 模块偷偷依赖 B 模块的内部实现细节那么未来任何一次重构都会变成连环爆炸。降低耦合的方法老生常谈却依然有效依赖倒置、接口隔离、事件驱动。用生活的例子讲你家里的插座是标准接口无论接什么电器都不用拆墙这就是面向接口编程如果每样电器都要单独拉一根专线那是面向实现编程改造一次全屋重新布线。内聚度同样重要。高内聚的意思是一起变化的代码尽量放在一起低内聚则是把随机的东西塞进同一个类。判断标准很简单如果两个函数放在同一个类里但彼此只是共享了一个文件路径没有任何业务关联那它们就不该住在一起。2.3 可测试性验证成本的数学题可测试性是我个人认为最真实的指标之一。一段代码能不能测试、测试成本高不高直接反映它的设计水平。判断方法很直接想让单元测试覆盖它我需要 mock 几个对象需要设置多少前置条件如果为了测一个函数你要先启动数据库、拉起消息队列、登录第三方系统那这个函数的设计几乎可以肯定存在结构问题。好的可测试代码有三个特征纯函数多也就是同样的输入永远有同样的输出没有隐藏状态依赖是注入的而不是在函数内部 new 出来的副作用被隔绝在外层业务逻辑里不直接操作网络、磁盘和时间。我见过很多团队说测试好难写其实大部分时候不是测试难是代码的依赖太多太难拆。反过来如果你把代码写成困难的测试那么你未来的排错也会同样困难——因为测试和调试本质上都是对代码行为的观察观察不到的地方出问题你就只能靠猜。写单元测试这件事能倒逼你改进设计这是很多团队没意识到的红利。2.4 性能与资源可控比快更重要性能维度最容易走极端。要么无限优化把毫秒级操作抠成微秒级要么完全无视复杂度在循环里嵌套查询数据库。我评判性能的标准不是快而是可控——也就是说面对数据量的增长运行时间能不能保持一个可预期的增长曲线。判断方法很直接看复杂度。嵌套的for循环里有几次循环每层遍历的数据量级是多少如果外层是十万量级内层又是百万量级这个算法大概率会在生产环境出事。这里不需要高深的算法知识只要养成分手复杂度习惯写没写数据库查询在不在循环里一次查询能解决的非要做 N 次这就属于不可控的性能隐患。资源管理是被低估的一环。数据库连接是不是随手建了没关文件流是不是用完忘记释放线程池大小是拍脑袋定的还是按实际 QPS 算的——这些细节平时看不见涌动时就是雪崩。性能维度最终要回答的问题是在可预见的负载增长下这套代码还能不能体面地工作。2.5 可扩展性新需求来临时你的姿势可扩展性可以直接用一个问题来衡量当一个新需求到来时你是加一个开关/加一个分支还是新增一个实现原有代码基本不动。后者的本质是遵守了开闭原则——对扩展开放对修改关闭。这里的核心是变化封装把变化的点隔离出来让每次新增都变成独立模块的加法而不是对既有逻辑的一次次手术。我用插件架构来解释这个过程核心框架从来不需要知道未来会有哪些插件它只需要定义好插件长什么样——接口和协议——然后让插件自己去实现。业务代码如果也能这样组织每次新功能就是新增一个模块不感动核心逻辑一毫。当然过度设计是扩展性的反面。别为了一个可能永远不会来的需求提前造出三层抽象。扩展性的判断标准不是抽象多不多而是当需求真的来的时候改动合不合理。这个度的拿捏需要经验我的经验法则是在第二次出现同类变化点的时候做抽象第一次和第三次之间做到不过早、不滞后。2.6 健壮性异常路径才是真实战场很多代码在晴天路径上跑得流畅一到下雨天就抛异常。判断健壮性关键是关注异常处理。一个不看异常处理就敢说代码很好的人见过生产事故就会闭嘴。健壮性的考察点包括外部依赖失败时能否优雅降级空数据、超大数据、非法输入进来时会不会崩错误信息是否包含了足够定位问题的上下文。我在评审里特别留意catch块的内容——如果你catch之后只打了一个日志然后继续往下走这和吞掉问题没有区别如果你catch到异常后调用一个可能同样抛异常的方法你就是在火上浇油。健壮性还体现在快速失败上。参数不合法就应该在入口直接报错而不是在传递了五层之后才暴露问题。故障应该在你眼皮底下炸开而不是在深层模块里悄悄腐烂。这个维度听起来偏防御性但生产环境的稳定性一大半是靠防御性编程撑起来的。2.7 安全性与合规沉默的底线安全维度在普通评审里常常被忽略直到出事才被想起。判断一段代码的安全性要看它对不被信任的输入是否持有戒心SQL 拼接有没有用参数化查询用户上传的文件有没有做类型和大小校验敏感数据有没有明文存储权限控制是在前端做样子还是在后端真正落地。合规也很容易被当作流程而不被重视但如果你处理的业务涉及用户隐私日志里都在输出身份证号那这就是埋在代码里的雷。安全合规维度没有太多花哨的技巧就是一条底线默认输入不可信默认敏感信息不可见默认权限必须校验。3. 给一段代码快速打分评审现场的判断顺序标准和框架有了还要解决如何上手的问题。很多人在评审时只会说这段代码不够好却说不出具体差在哪里。这里分享我的一套判断流程按执行顺序展开。这套流程也适用于你自己写完代码后进行自查。第一遍骨架扫描约 30 秒只看整体结构不看细节。先数一下这个模块里有多少个文件每个文件的职责是什么然后看函数层级有没有超过 30 行的大函数最后看数据流从入口到出口数据经过了哪几层。我在这阶段常用的判断问题是如果要把这个功能点抽出来单独使用我能只抽一个类吗如果答案是不能或者抽出来之后要拖上一堆依赖说明这个模块的边界有问题。第二遍热点排查约 2-3 分钟有经验的人会在几秒钟内锁定问题高发区命名是否含糊每个函数的参数数量是否过多超过 3 个就要警惕有没有大段重复代码异常被 catch 后如何处理有没有直接在业务代码里 new 出依赖对象。这一遍的重点不是逐行读而是寻找异味。我总结了一个快速问题清单可以打印出来贴在显示器上变量名是否表达了业务含义函数有超过一个职责吗有没有魔法数字一个改动会影响多少个调用方异常处理是吞还是报资源有没有显式关闭循环里有网络请求或 SQL 吗输入参数有没有做校验第三遍变更模拟评审代码时我习惯做一次思维实验把用户故事里最常见的三个变更场景套进去——加一个字段、加一种状态、加一个分支看看代码需要改动几处。改动越少设计越好。举个例子我曾经看到过一段处理订单状态的代码业务新增了一个已取消状态结果是改了两个枚举、三个if判断、一个数据库映射、一个前端下拉框。实际上如果能用一个状态机模式把状态的流转收敛到一个地方这个需求只要动一个配置表就够了。第三遍这个变更模拟比任何理论分析都要直接。一个重构前后对照我用一个简化的片段来说明打分标准在实际中的表现。假设有这样一个函数def process(data, flag): if flag: if len(data) 0: result [] for i in range(len(data)): if data[i] 3: temp calc(data[i]) result.append(temp) return result else: return [] else: return []第一遍扫描函数名process完全没有信息量参数flag是什么意思要靠猜三层缩进往右塌方。第二遍排查魔法数字 3temp命名敷衍分支逻辑冗长且绕。第三遍变更模拟如果过滤条件从 3 变成 5我要找到那一行数字改掉未来谁来找重构之后def process_active_items(raw_items, filter_enabled): if not filter_enabled or not raw_items: return [] return [item.calc() for item in raw_items if item.is_active()]虽然这段代码依然简单但每一行都在传递语义filter_enabled告诉你开关的含义item.is_active()把哪个条件算活跃圈在一个方法里列表推导式把过滤和转换的关系表达得很清楚两个早返回替代了多层嵌套。打分差距就在这些细节上拉开了。4. 高质量代码能力的养成可操作的训练路线知道质量标准只是第一步真正难的是写得出来。这部分把能力拆成五个可以刻意训练的模块每个都有具体的操作建议。4.1 动手前的黄金十分钟先设计再动手大多数烂代码源于什么都没有想清楚就开始敲键盘。我养成的一个习惯是在接到稍复杂的任务时先花十分钟做设计草案哪怕只是画几行伪代码、列几个关键类和数据流。设计阶段要回答三个问题输入和输出是什么核心业务规则有哪些变化最频繁的点在哪里。回答完这三个问题代码的骨架基本就定了。十分钟的设计通常能省下后面几个小时的返工而且能显著减少写着写着发现结构不对的推倒重来。4.2 重构练习从能跑到好看的刻意训练能力提升最快的路径之一是拿自己一周前、一个月前的代码做重构练习。选择一段已经在跑的代码在不改变行为的前提下试着优化它的结构把大函数拆小为变量重新命名把重复代码抽取出来为隐式逻辑补上意图注释。重构练习的关键是一个字狠。不要舍不得删不要觉得这段虽然丑但是跑得好好的就不动。真正的高手都练过几次大动干戈的重构。而且重构的过程会让你体会到结构对行为的影响这种体会是任何理论课都给不了的。每次重构完用第 2 节那七个维度复盘一遍你会发现自己越来越敏感。4.3 读好代码的正确姿势带着问题读而不是通读源码阅读经常被当成一种学习方式但大部分人只是看了一遍收获甚少。正确的读法应该是带着问题去读看一个优秀的开源项目不要从头到尾通读而是选定一条完整的数据流——比如一个请求从入口到数据库再返回追踪它的路径观察每个环节的处理方式和抽象取舍。带着问题读还有一个跟进策略每读完一个模块合上编辑器在纸上把它的数据结构画出来把接口之间的关系写下来看看自己能不能复述。如果不行就说明没读透。这种费曼式的源码阅读比反复看一百遍有效得多。我常用的练习量是每周至少深入一个模块坚持两个月就会明显感觉到设计手感的提升。4.4 评审与被评审双向的成长杠杆代码评审是团队里最现成的质量提升场景。当你作为评审者你训练的是判断力当你的代码被评审你训练的是接受力和自省力。我见过太多人把评审当成找茬或者应付然后错过了这个成长杠杆。评审者要想真正有效必须做到给标准、给理由、给方案。只说这段代码不好没有价值要说清楚它的可读性有问题因为变量名没有含义建议拆成两个函数因为责任不单一。这个要求的背后是在强迫你把模糊的感觉转化为具体的质量维度——这本身就极其锻炼判断力。被评审的一方要牢记一个心态别人指出质量问题不是对你的能力侮辱而是帮你提前排雷。我有个小习惯每次被评审提意见都随手记录在案定期复看。三个月后再回看那些意见你会明显发现自己犯的重复错误越来越少。4.5 建立个人检查清单Checklist高质量代码不是靠灵感而是靠习惯。我把第 3 节的问题清单做成一个自检测试表每完成一个模块就快速过一遍。这个动作看起来简单作用却非常大。因为在代码写完后的一小时里你对自己刚写的东西带有天然的智力滤镜——看着哪哪儿都顺眼只有靠清单这种外部参照物才能把你拉回客观位置。检查清单要持续迭代每踩一个坑就往里面加一条比如登录接口有没有做验证码防爆破时间一长这就是属于你自己的高质量标准底稿。5. 落到日常工具、门禁与人的判断判断代码质量和提升代码能力不能只靠自觉还需要工具的辅助和团队的流程约束。工具能做的是尽量把那些可以客观测量的指标前置把问题挡在合并到主干之前。5.1 静态检查与复杂度分析第一层工具是静态检查包括但不限于各种 linterESLint、Checkstyle、Pylint、golangci-lint 等和静态分析平台SonarQube 是常用的代表。它们能自动识别命名风格问题、重复代码、空引用风险、魔法数字、圈复杂度超限等大量坏味道。圈复杂度是我特别推荐关注的一个数值。一个函数的圈复杂度越高意味着它的逻辑分支越多越难理解和测试。很多静态分析平台会直接标出圈复杂度超标的函数这种机械化的问题根本没有讨论价值——看到就该拆。我经常建议团队在 CI 里加一道门禁圈复杂度超过阀值就直接阻断合并省得评审时反复浪费口舌。5.2 测试覆盖率的正确用法另一个常用工具是覆盖率统计但要先说清楚一个常见误解高覆盖率不等于高质量低覆盖率也不等于一定低质量。覆盖率是把双刃剑它的合理用途是辅助发现完全没被测试覆盖的危险区域而不是制造一个冰冷的百分比 KPI。我建议的落地姿势是关注核心业务逻辑的覆盖。那些充满了 if-else 的分支、异常处理的路径才是覆盖重点而简单的 getter、setter、配置类覆盖率高低没有多少实际意义。把覆盖率工具当作地图来用而不是当成绩单来用这句好使。5.3 从 Gate 到文化质量是团队的事流程上可以做的是把质量检查从人肉驱动变成门禁驱动合并代码前必须通过静态检查、必须通过测试、必须经过至少一个合格评审者。门禁的价值在于把最低标准自动化避免质量参差取决于当天评审人的心情。但再聪明的工具也无法取代人的判断。工具量化的是表象——命名可检查风格却不理解语义覆盖率能算百分比却测不出一个艰难捉襟的错误边界。真正决定代码质量的是人愿不愿意在差不多的地方再多花十分钟把那个变量名改得更准确在那个异常分支里再多打一条上下文日志。团队如果能形成这种对质量有羞耻感、有洁癖的文化氛围比上十套工具都管用。我个人的体会是代码质量不是一次性的大工程而是一个一个细小的选择累计出来的结果。今天这个函数是叫handleData还是叫validateAndSaveOrder这段异常是吞掉还是带上上下文重新抛出新增功能时是再加一个 if 还是抽出新的策略类——每一次看起来都无关紧要但时间一长优秀和平庸的距离就在这些选择里被逐步拉开。如果这篇文章只能留下一句话我希望是这句把自己当成三个月后接手这份代码的陌生人。该写的意图写清楚该拆的结构拆干净该处理的边界处理妥帖。三个月后的你会在深夜里感谢现在这个肯多花十分钟的自己。
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻