微信扫码
添加专属顾问
我要投稿
从初入职场到资深开发者,作者分享了对好代码认知的成长历程,揭示代码质量的多维评价标准。 核心内容: 1. 职业发展不同阶段对好代码理解的演变 2. 好代码的四大评价维度:稳定、体验、效率、成本 3. 编写好代码的实践原则与常见误区
本文围绕“什么是好代码”展开,作者结合自身职业发展阶段,从初入职场时仅关注完成任务的“黑盒认知”,逐步过渡到深入思考代码质量的多维度评价标准。文章提出,好代码不仅需满足功能需求和稳定性,还应兼顾用户体验、开发效率、可维护性与成本控制,并引用“金码奖”评审标准,从稳定、体验、效率、成本四个维度进行量化分析。作者进一步强调,写好代码需要全局视角,遵循设计原则(如开闭原则)与设计模式(如责任链),避免“坏味道”,同时警惕过度分层和复杂框架对可读性与维护性的负面影响。最终指出,好代码是权衡艺术的体现,其标准随个人成长与团队共识不断演进。
前言
什么是好代码
刚入职的时候,我们对新的工作环境,协作关系,系统结构等都比较陌生。周围的环境对我们来说是一个黑盒,我们不知道系统是如何交互的,交互的协议是怎么一步步建立起来的,有哪些特殊的场景,所以只能在局部小心意义地添加代码,生怕出事。
回忆那个时候,我当时首先考虑的是能够完成任务,思考的几个问题如下:
代码应该写在哪里? - 期望:理解链路 实际:问师兄
代码应该怎么写? - 期望:理解上下文 实际:拷贝周围
代码不要出错? - 期望:做好边界判断 实际:review 被提示
代码可以回滚? - 期望:做好开关 实际:过于自信
循环:开关应该怎么弄? - 重复 1 2 3
那时候,理解的好代码,是最基础的要求:支持需求,不要出错。
后来逐步开发,了解增多后,开始反转了:不知道的变成少数(注:某些特定系统,不敢说全链路....), 知道的变成了多数。这时候,我们能够思考的内容就变多了,同时开发的体验也增多了,就会产生一些疑问:
支持需要? 这次是支持了,但是造成下次改动更加困难了, 这样的代码是好代码么?
不要出错? 业务功能可能没有出错,但是可能造成性能问题, 这样的代码是好代码么?
我们渐渐从单一的完成任务,开始思考如何更好地完成任务,从短期的设计开始考虑长期的感受,从单一的指标开始考虑更多的因素,那么好代码的答案会是什么呢?我们可以继续往下探讨。
理解代码是好代码还是坏代码,可能是比较困难的一件事情,因为考虑的因素有很多,每个人的看法也不太一样,那该如何评价呢?
下面的一个评价标准,可能是抽象到最高程度了:
Martin(Bob大叔)曾在《代码整洁之道》一书中说:当你的代码在做 Code Review 时,审查者要是愤怒地吼道:“What the fuck, is this shit?”、“Dude, What the fuck!”等言辞激烈的词语,那说明你写的代码是 Bad Code,如果审查者只是漫不经心的吐出几个:“What the fuck?”,那说明你写的是 Good Code。
图片来自《我心中的好代码》
虽然比较抽象,但是我们还可以从载体去看,如果需要评价,那么就需要衡量的指标,要产生对应的指标,就需要被观察到,从可被观察到的角度看,我们可以看看代码的一些动作:
代码被观察
当我们上线服务的时候,代码会被 「运行」,产生系统运行指标,同时也会影响用户体验。
系统指标:比如耗时很长,可能没有单元化调用,跨单元了;
用户体验:比如可能提示的错误码就是 System Error, 用户难以理解。
当我们遇到问题的时候,代码会被「排查」,让我们感受到排查难以程度。
排查难易:比如很难找到关键日志,找到日志后,代码也没有很好的分块,有很多 if else。
当我们遇到类似需求时,代码会被「复用」,会影响复用的成本。
复用成本:如果责任不单一,抽象粒度不够,就可能会引起较多老代码修改,增加风险和变更压力。
上面列举了评价的一些维度,进一步看,我们想要的这个评价不仅仅是单个人的观点,更是大家的共识。就比如一道菜,你说不咸,但是其他人都说咸,整体来说就是偏咸的。所以我们得意识到个人的判断是带有主观性的,大家的共识可能更客观一点。
之前参加了部门“金码奖”代码评审,所以这里也简单分享一下评审规则,也感受一下大家的共识:
好代码的评选标准回到业务价值上 — 体现在稳定、体验、效率、成本四个维度 | ||
角度 | 解释 | 关键点举例 |
稳定40分 | 代码在运行过程中具备高可用性和容错能力...... 从而保障业务连续性和系统连续可用。 | 错误处理&边界保障:
监控日志完备:
冗余设计&版本控制:
|
体验30分 | 代码实现是否能够提升用户的满意度,包括功能易用性、性能表现、交互流畅性、新用户体验创造等。 | 性能优化适配:
用户友好反馈:
...... |
效率20分 | 指代码能否高效支持业务迭代和运行,包括:
| 性能优化:
维护效率:
可扩展性 &业务提升便捷:
|
成本10分 | 指在资源、运维、人力成本等维度的降低或优化,包括直接投入和隐性运维成本。 | 资源优化:
维护成本&知识传递:
测试成本:
|
如何写好代码
有了一个基本的认知后,下面将结合一些大家和个人的总结,再一起看看写好“好代码”的心得。
“神之一手”是源自日本动漫《棋魂》的围棋术语,指棋手在关键时刻以超乎常理的技艺走出扭转全局的一步,象征围棋技艺的最高境界。
对于每一步棋来说,我们只能看到局部的影响(受限于我们能想到后面几步),当我们事后站在全局的角度看,有可能的“平平无奇”的一步却起着关键性的作用。对代码里来说,我理解的“好代码”,应该在更大的范围来看,确保每一步在大局中的“精确性”,不“草率落子”,也不“过度设计”,让大家感觉“恰到好处”,很舒服。
为了理解当前的代码,我们要站在更高的层次,从开发的角度看,关注点可以有这么几个层次:
《程序员的自我修养 - 架构主题简思》
设计模式:关注类之间的关系,探究类之间如何协作完成信息传递与计算。
应用内架构:关注应用内的结构,可以体现在模块间的设计上。
应用间模式:关注应用间协作,将功能划分到不同应用中,设计应用间协作完成复杂流程。
业务架构:关注系统间协作,将商业活动分解到不同系统中,通过系统协作支撑价值流。
关于如何具体如何养成标准,我找到了一遍比较好的文章 《我心中的好代码》,里面引用了很多内容,可以大家在做好每一步时对齐的标准:
《我心中的好代码》 | 个人经历映射 | ||
范围 | 关注点 | 实践参考举例 | 我的文章举例 |
点 | 变量、方法、类 | 《阿里经济体开发规约》 开源版本:阿里巴巴java开发手册 https://github.com/alibaba/p3c | 《[实验] 金额对象类型问题》 |
线 | 方法、类之间关系及数据结构 | 《Java设计模式:23种设计模式全面解析(超级详细)》https://c.biancheng.net/design_pattern/ | 《设计模式六大原则理解》 |
面 | 服务/应用(模块之间耦合关系) | 《应用架构之道:分离业务逻辑和技术细节》 | 《应用架构实践 - 简洁应用框架 VSEF》 |
体 | 从业务中高度抽象出本质模型 | 《复杂性应对之道 - 领域建模》 | 《DDD的关键理解》 |
前面一直在介绍“好代码”,其实避免“坏代码”,我们也就能把代码往好的方向推进了。下面会介绍一下代码的“坏味道”,大家可以一起看下体会下。下面的例子来自《消灭Java代码的"坏味道"》。
案例1:L大小写
在使用长整型常量值时,后面需要添加L,必须是大写的L,不能是小写的l,小写l容易跟数字1混淆而造成误解。
反例:
long value = 1l;
正例:
long value = 1L;
案例2:不要使用魔法值
当你编写一段代码时,使用魔法值可能看起来很明确,但在调试时它们却不显得那么明确了。这就是为什么需要把魔法值定义为可读取常量的原因。
反例:
for (int i = 0; i < 100; i++){ ...}if (a == 100) { ...}
private static final int MAX_COUNT = 100;for (int i = 0; i < MAX_COUNT; i++){ ...}if (count == MAX_COUNT) { ...}
案例:频繁调用Collection.contains方法请使用Set。
在java集合类库中,List的contains方法普遍时间复杂度是O(n),如果在代码中需要频繁调用contains方法查找数据,可以先将list转换成HashSet实现,将O(n)的时间复杂度降为O(1)。
反例:
ArrayList<Integer> list = otherService.getList();for (int i = 0; i <= Integer.MAX_VALUE; i++) { // 时间复杂度O(n) list.contains(i);}
正例:
ArrayList<Integer> list = otherService.getList();Set<Integer> set = new HashSet(list);for (int i = 0; i <= Integer.MAX_VALUE; i++) { // 时间复杂度O(1) set.contains(i);}
案例:禁止使用构造方法BigDecimal(double)。
BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
反例:
BigDecimal value = new BigDecimal(0.1D);//0.100000000000000005551115...
正例:
BigDecimal value = BigDecimal.valueOf(0.1D);//0.1
学习的内容,如果能和我们的工作内容相关联,那么我们能够吸收的更快一点。写好代码的一个比较重要的部分在于掌握常用的设计原则和模式。
关于设计原则和设计模式,我之前也在交易链路上尝试寻找,进行进一步的理解,具体文章可以参考 《浅谈交易链路中的一些设计原则&模式》,下面会举一两个例子来体会一下多个类/模块之间的合作设计。
开闭原则,在面向对象编程领域中,规定“软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的”,这意味着一个实体是允许在不改变它的源代码的前提下变更它的行为。
实践举例:订单列表中,平台定义需要展示店铺图标的框架,业务可以自助定制店铺图标。
责任链是说将请求让队列内的处理器一个个执行,直到找到愿意执行的。
商业能力扩展、域扩展,在执行回收结果的时候,会遍历实现的插件,并结合回收规则,进行及时的熔断。这和责任链的逻辑是类似的。实践举例:以确认收货打款时“是否跳过通知支付”为例(如:有些是即时到账场景),执行引擎会遍历插件的实现,找到第一个返回要 true(跳过)的结果时,就会停止执行,整体返回 true。
很多时候,代码怎么写是受到很多因素影响的,取决于我们想要的目标,当然也需要承担对应的成本。就像下面的神经网络一样,最终输出的结果是多种因素变量 和 权重 结合的复杂结果,是一个取舍。
▐ 4.1 层次是否过多
计算机科学中的所有问题都可以通过增加一个间接层来解决。
好的分层,可以让大家遵循统一的分解过程,在任务维度进行更好地复用。在结构上也更清晰。
但是“成也萧何,败也萧何”,交易链路上,比较痛苦的地方在于:层次过多! 有系统应用、服务入口、流程服务、活动节点、领域服务、领域能力、领域扩展、商业能力扩展、商业能力实现这一系列层次。
这样的层次,导致透传一个上下文,需要层层透传,进行打包,搞个一天起步,所以也经常看见基于 ThreadLocal 这样的缓存方案。这种透传是否合理呢?层次多到一定程度之后,是不是理解负担进一步加重?
▐ 4.2 易理解性的挑战
代码的易理解性是最朴素的要求,但是做好这一点也非常有挑战性。
首先,我们看一下一个轻量脚本框架的理解,看看其对可读性的影响。轻量脚本的优缺点如下:
好处:能够在一个配置化后台,用比较简单的配置动态生成脚本,减少打包代码部署流程较长的烦恼。
缺点:关键的业务逻辑被包装在了一个脚本化框架体系内,需要额外的学习成本。业务开发人员解释不了整体的逻辑,需要找脚本框架维护人员进行答疑。
易理解性的挑战1 - 脚本化
个人观察:
在层次较多的系统中,谨慎增加额外层次(灵活性的收益 是否能够 覆盖理解成本);
引入框架,需要判断框架的可维护性,确认组织意志;
考虑长期的维护效率,而不是某次的发布。
除了层次,非功能性需求对易于理解性的挑战也很大。下面在模拟了一些演化情况,来看看易理解性的挑战:
易理解性的挑战2 - 非功能性要求
个人观察:
好的代码应该隔离关注点,把真正的业务逻辑呈现出来;
性能、可用性等实现应该作为一些切面、策略独立出来,可选可去;
代码在边界场景上的正确性,以及可读性处理,是一个比较进阶的阶段。
好的代码标准,随着考虑因素的变化,也在不停地调整。我们的工作经历,其实就是在不停地调整各个因素的权重,最终会给出我们的一个判断。
通过团队的多种判断碰撞,形成一个团队的共识,如果符合团队的共识规则,我想应该是一个比较基础的答案。更好的代码出现,改变组织效率,带来业务创新的情况,取决于各个人的发展情况和历史机遇。总而言之,你所在的位置,定义了你会写出怎么样的好代码。
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2025-08-26
百度橙篇 AI:一站式创作神器,新手也能秒变大神!
2025-08-26
AI在ToB领域的效率革命、预算重构与战略选择
2025-08-25
小心!你的思考力可能正在被AI“外包” | 创业Lifestyle
2025-08-24
我为什么不建议用AI读文献?
2025-08-23
Excel制图VS AI图表生成器:效率差不止10倍
2025-08-23
AI时代的人效评估指南——如何终结内卷与伪高效
2025-08-22
用 AI 三步搞定专业排版:从Prompt到代码,一键生成惊艳杂志风
2025-08-22
【对谈】YouMind 玉伯:剪藏就是 AI 时代的点赞
2025-07-28
2025-08-06
2025-06-12
2025-06-23
2025-06-18
2025-06-08
2025-06-09
2025-06-08
2025-06-30
2025-07-08