《编写可读代码的艺术》:代码是写给人看的,只是偶尔让机器执行
Dustin Boswell 和 Trevor Foucher 的《编写可读代码的艺术》(The Art of Readable Code)是一本我后悔没早点读的书。
它只有 200 页,薄薄一本。但每一章都解决一个我曾经踩过的坑。它不教你写更"聪明"的代码——它教你写更易读的代码。
书里有一句话让我反复回味:
代码是写给人看的,只是偶尔让机器执行。
这个观念让我重新审视了过去 10 年写过的所有代码。
核心论点:代码首先是给人看的
很多工程师(包括以前的我)以为写代码的目标是让机器执行。于是我们追求"高效"、“简洁”、“巧妙”,写出各种炫技的算法、一行解决的复杂表达式。
但代码的真实生命周期是:
- 第 1 天:写代码 → 机器执行
- 第 30 天:别人读代码 → 改 BUG
- 第 180 天:新人接手 → 试图理解
- 第 365 天:原作者都读不懂 → 重构
代码被读的次数,远远超过被执行的次数。所以代码的首要目标不是"机器执行",而是"人能读懂"。
Boswell 和 Foucher 把这个观念拆成了 14 个具体的原则——每一个都能立即应用到实际工作中。
四个核心原则
原则 1:表面层次的改进(命名 + 注释 + 格式)
好名字 > 好注释。一个好的命名能让代码"自我解释"。
反面例子:
def calc(x, y):
return x * 0.5 + y * 1.2正面例子:
def calculate_total_price(base_price, tax_rate):
return base_price * (1 - DISCOUNT_RATE) + base_price * tax_rate后者多打了几个字,但读起来完全不需要思考。这种"用文字换时间"的投资,回报是十倍百倍的。
命名要"精确"而非"短"。d 短但没意义;elapsed_time_in_days 长但精确。代码不是赛跑——长名字赢得是理解的速度。
原则 2:简化循环和逻辑
把复杂的 if-else 拆成多个小函数。一个 50 行的函数谁都看不懂;五个 10 行的函数,每个一眼就明白。
反面例子:
if (user && user.profile && user.profile.age >= 18 && !user.banned) {
// ...
}正面例子:
if (isAdultActiveUser(user)) {
// ...
}判断逻辑被封装后,主代码变成了"读文章"。
原则 3:重新组织代码(按"故事"顺序)
代码的阅读顺序,应该和它的执行顺序一致。
反面例子(先处理边界,再处理核心逻辑):
if not user: return None
if not user.email: return None
user.validate()
# ... 核心逻辑 ...正面例子(先核心,后边界):
user.validate()
# ... 核心逻辑 ...
if not user or not user.email:
return None读者顺着代码读下去,先看到主线,再看到分支——这符合人的自然阅读习惯。
原则 4:抽取"问题相关的子领域"
这是最反直觉但最有价值的原则:用业务语言包装代码。
# 反面:直接表达技术
cell = Cell()
cell.set_text("Hello")
cell.set_color("red")
# 正面:用业务语言包装
def highlight_cell(text, color):
cell = Cell()
cell.set_text(text)
cell.set_color(color)
return cell
highlight_cell("Hello", "red")底层逻辑没变,但业务代码读起来像英语句子。这就是"抽象"的本质——把细节藏在名字后面。
一些让我重新审视习惯的细节
1. 注释要解释"为什么",不是"是什么"
# 反面:注释代码做了什么
i += 1 # i 加 1
# 正面:注释代码为什么这么做
i += 1 # 跳过标题行,处理下一条数据“是什么"代码本身就告诉读者了,注释是浪费。“为什么"才是读者想知道的——这个判断背后的原因、考虑、权衡。
2. 用名字表达"意图”
# 反面
result = []
for x in items:
if x > 10:
result.append(x)
# 正面
big_items = [x for x in items if x > 10]result 没意义,big_items 一目了然。好名字自带注释。
3. 控制流要"线性”
# 反面:嵌套深
if (a):
if (b):
if (c):
do_something()
# 正面:早返回
if (not a): return
if (not b): return
if (not c): return
do_something()人脑能轻松处理 3 层以内的嵌套,4 层以上就开始乱。早返回把嵌套变成线性。
4. 变量定义靠近使用位置
# 反面
def process():
a = 1
b = 2
c = 3
# ... 50 行其他代码 ...
result = a + b + c
# 正面
def process():
# ... 50 行其他代码 ...
a = 1
b = 2
c = 3
result = a + b + c读代码的人不用滚动屏幕去查 a b c 是什么——它们就在眼前。
对我工作方式的影响
读完这本书后,我养成了几个习惯:
习惯 1:写代码前先想名字
以前我先写实现,再起名字。现在反过来——先想好变量和函数的名字,再写实现。
这看似多了一步,实际上省了很多重构时间。名字想不清楚,往往意味着逻辑没想清楚。
习惯 2:写代码时想象读者
我会在心里有一个"未来的同事"——他在读我的代码,看他能不能一眼看懂。如果不能,我就改。
习惯 3:定期重读自己的代码
每月花一个下午,重读自己 3 个月前写的代码。如果看不懂,那就写得不够好。
习惯 4:优先重构"可读性差"的代码,而不是"性能低"的代码
性能瓶颈通常只占代码的 5%,但可读性差的代码会让整个团队减速 50%。我宁愿牺牲一点性能,换取可读性。
一个反直觉的发现
Boswell 和 Foucher 在书里提了一个让我意外的数据:
大多数代码被阅读的次数,比被修改的次数多 10 倍以上。
这意味着优化"可读性"的回报,远超优化"性能"。一段性能高但难读的代码,最终会被扔掉重写;一段性能一般但易读的代码,会被不断修改延长生命周期。
长期看,“易读"就是"高效”。
与其他编程书的对比
我读过的编程书大致分两类:
1. 教"做什么":算法、数据结构、设计模式
2. 教"怎么做":具体语言、框架、工具
这本书是第三类——教"怎么写别人能看懂"。
这类书很少,因为"写可读代码"听起来不够"硬核"。但它实际上是最有杠杆率的技能——代码被读的时间是被写的时间的 10 倍以上。
会写可读代码的工程师,能用 1/10 的沟通成本完成协作。能用 1/10 的认知成本接手新项目。能用 1/10 的风险维护老系统。
这不是软技能,这是核心技能。
写在最后
我推荐每个工程师都读一遍这本书——不管是新手还是老兵。
新手会学到"代码是写给人看的"这个基本观念;老兵会发现自己 10 年养成的坏习惯,很多可以用书里的原则 30 秒改掉。
读完后,你可能会像我一样,对自己过去 10 年写的代码感到脸红。但这正是进步的开始。
补充:微信读书里我划过的句子
这本书我读得很仔细,但奇怪的是,我在微信读书里没有划任何具体的句子。可能是因为书里每一条原则都已经是"操作清单",没什么需要我反复回味的金句。
但有一个整体感受我想分享:这本书改变了我对"代码 review"的态度。
以前我做 code review,重点是"找 bug"、“挑性能问题”、“质疑设计”。读完后我意识到,更重要的是"问这段代码别人能不能读懂"。
如果一段代码能跑但没人能懂,那它就是**“技术债”**——不是欠银行的钱,是欠未来同事的"理解债"。
现在我做 code review,会经常问作者:
- 这段代码你想表达什么?
- 如果一个新人读这段代码,他能理解吗?
- 哪些命名可以更精确?
这些问题比"这里有空指针"更重要。因为空指针 BUG 能在测试中发现,而"理解债"会在未来 6 个月里持续消耗团队的精力。
如果你写代码 5 年以上,没读过这本《编写可读代码的艺术》,强烈建议你读一遍。
它会改变你看代码的方式。
推荐阅读
- Dustin Boswell, Trevor Foucher《编写可读代码的艺术》(The Art of Readable Code)—— 本书
- Robert C. Martin《代码整洁之道》(Clean Code)—— 更全面的代码质量原则
- Martin Fowler《重构》(Refactoring)—— 具体重构手法
- Steve McConnell《代码大全》(Code Complete)—— 经典编程百科
- Tom Long《软件设计哲学》(A Philosophy of Software Design)—— 关于"复杂性"的深度思考