1-2-1-代码是沟通而不是写给机器的
1.2.1 代码是沟通,而不是写给机器的
一个反直觉的真相
大多数人学编程时被告知:"编程就是告诉计算机做什么。"
这话没错,但不完整。更准确的说法是:
编程是告诉其他人,你想让计算机做什么。
计算机不在乎你的代码是否优雅、是否清晰、变量名是否有意义。只要语法正确,它都能执行。
但人在乎。
代码的两个读者
每一段代码都有两个读者:
1. 编译器/解释器(机器)
机器需要的:
- 语法正确
- 类型匹配(在静态类型语言中)
- 逻辑可执行
机器不在乎的:
- 变量名是
user还是u还是asdfgh - 缩进是 2 空格还是 4 空格
- 有没有注释
- 函数是叫
calculate_total还是func1
2. 人(包括未来的你)
人需要的:
- 理解代码在做什么
- 理解为什么这么做
- 理解如何修改
- 理解边界情况如何处理
人在乎的:
- 变量名是否表达了含义
- 结构是否清晰
- 逻辑是否易于跟踪
- 有没有解释复杂决策的注释
对比:写给机器的代码 vs 写给人的代码
示例1:计算购物车总价
写给机器的代码:
def f(c):
t = 0
for i in c:
t += i['p'] * i['q']
if i['p'] * i['q'] > 100:
t -= (i['p'] * i['q']) * 0.1
return t
机器的理解:完全没问题,可以执行。
人的理解:
f是什么函数?c是什么?i['p']和i['q']是什么意思?- 为什么大于 100 要减去 10%?
- 这个 10% 是折扣吗?
需要花费 2-3 分钟理解。
写给人的代码:
def calculate_cart_total(cart_items):
"""
计算购物车总价,单个商品满100元打9折
Args:
cart_items: 购物车商品列表,每个商品包含 price 和 quantity
Returns:
总价(已应用折扣)
"""
total = 0
for item in cart_items:
item_subtotal = item['price'] * item['quantity']
# 单个商品满100元,享受9折优惠
if item_subtotal > 100:
discount = item_subtotal * 0.1
item_subtotal -= discount
total += item_subtotal
return total
人的理解:30秒内完全明白。
代价:多了几行代码,多了几个字符。
收益:
- 6个月后的你能立即理解
- 新同事能快速上手
- 需求变更时知道改哪里
- Code Review 时能快速发现问题
示例2:验证邮箱格式
写给机器的代码:
const v = (e) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);
写给人的代码:
function isValidEmail(email) {
// 简单的邮箱格式验证
// 格式:任意字符 + @ + 任意字符 + . + 任意字符
// 注意:这不是完整的 RFC 5322 验证,只是基本检查
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
额外信息:
- 函数名清楚表达了意图
- 注释说明了验证的程度(不是完整的RFC标准)
- 给正则表达式起了个有意义的变量名
沟通的多个维度
代码作为沟通工具,传递多个维度的信息:
1. What(做什么)
通过代码本身传达:
# 清楚:代码自解释
active_users = [user for user in users if user.is_active]
# 不清楚:需要猜测
result = [x for x in data if x.status == 1] # status=1 代表什么?
2. Why(为什么)
通过注释传达:
# 清楚:解释了业务原因
# 根据财务部门要求,订单金额需要保留2位小数
# 避免出现 0.1 + 0.2 = 0.30000000000000004 的精度问题
order_total = Decimal(str(subtotal)).quantize(Decimal('0.01'))
# 不清楚:没有解释为什么
order_total = Decimal(str(subtotal)).quantize(Decimal('0.01'))
3. How(如何做)
通过清晰的结构传达:
# 清楚:分步骤,每步都明确
def process_order(order):
validate_order(order)
apply_discount(order)
calculate_tax(order)
charge_payment(order)
send_confirmation(order)
# 不清楚:所有逻辑混在一起
def process_order(order):
if order.items and order.user:
t = 0
for i in order.items:
t += i.price
if order.coupon:
t = t * 0.9
t = t * 1.1
payment.charge(order.user.card, t)
email.send(order.user.email, "ok")
4. When(什么情况下)
通过条件判断的命名传达:
# 清楚:条件有明确含义
if is_premium_user(user) and has_valid_subscription(user):
grant_access()
# 不清楚:魔法数字和不清楚的条件
if user.level > 3 and user.status == 1:
grant_access()
真实案例:代码即文档
案例:Stripe 的 API 设计
Stripe 的 API 被认为是业界最清晰的 API 之一。看看他们的代码风格:
# Stripe 的设计
stripe.Charge.create(
amount=2000, # 金额:20.00美元
currency="usd",
source="tok_visa",
description="Charge for test@example.com"
)
# 如果是"写给机器的"风格
stripe.c(2000, "usd", "tok_visa", "Charge for test@example.com")
为什么 Stripe 成功?
因为他们理解:API 的调用者是人,不是机器。 清晰的 API 能降低学习成本,减少出错,提高开发者满意度。
案例:Linux 内核的注释文化
Linux 内核以代码质量著称,其中一个重要原因是严格的注释规范:
/*
* We don't want to keep the lock while loading the file from disk,
* so we release the lock, load the file, then re-acquire the lock.
*
* This is safe because the file content is immutable once written.
*/
unlock(&cache_lock);
data = load_from_disk(filename);
lock(&cache_lock);
注释解释了:
- 为什么要释放锁(避免持锁时间过长)
- 为什么这样做是安全的(文件内容不可变)
这些信息机器不需要,但对理解代码的人来说至关重要。
对使用 AI 的程序员的特别建议
AI 生成的代码往往是"机器友好"的,但不一定是"人类友好"的。
AI 代码的常见问题
-
变量名过于简洁
# AI 可能生成 def proc(d, u, t): return calc(d, u) * t # 应该改成 def process_transaction(data, user, tax_rate): base_amount = calculate_base(data, user) return base_amount * tax_rate -
缺少解释复杂逻辑的注释
# AI 生成 result = sorted(items, key=lambda x: (x.priority, -x.created_at)) # 应该改成 # 排序规则: # 1. 优先级高的在前(priority 小的在前,1 > 2 > 3) # 2. 相同优先级时,创建时间晚的在前(降序) result = sorted(items, key=lambda x: (x.priority, -x.created_at)) -
函数做了太多事情,但名字不能体现
# AI 生成 def get_user(id): user = db.get(id) log_access(user) update_last_seen(user) return user # 应该拆分或改名 def get_and_track_user(id): """获取用户信息,并记录访问日志、更新最后访问时间""" user = db.get(id) log_access(user) update_last_seen(user) return user
改进AI代码的流程
- 生成后必须重命名:把所有过于简洁的变量名改成有意义的
- 添加关键注释:解释"为什么",而不是"是什么"
- 检查函数职责:一个函数是否做了太多事情
- 添加文档字符串:说明参数、返回值、可能的异常
实践原则
1. 变量名即文档
| 不好 | 好 | 更好 |
|---|---|---|
t |
total |
cart_total_after_discount |
d |
data |
user_profile_data |
i |
item |
cart_item |
f |
flag |
is_premium_user |
例外:在非常小的作用域内(如循环中的 i, j),简短名字是可以接受的。
# 可以接受
for i in range(10):
print(i)
# 应该改进
for i in users:
process(i)
# 更好
for user in users:
process_user(user)
2. 函数名即规格说明
函数名应该回答:
- 做什么(动词开头)
- 对什么对象(名词)
- 返回什么(或修改了什么)
# 不够清楚
def user(id): ...
# 更清楚
def get_user(id): ...
# 最清楚
def get_active_user_by_id(id): ...
3. 注释即设计文档
好的注释解释:
- 为什么这样做(业务原因、性能考虑)
- 为什么不那样做(排除了哪些替代方案)
- 有哪些边界情况或限制
不好的注释重复代码:
# 不好的注释
# 获取用户
user = get_user(id)
# 好的注释
# 这里使用缓存的用户信息,而不是实时查询数据库
# 因为用户信息更新频率低(每天 < 10次),而读取频率高(每秒 > 1000次)
# 缓存过期时间设置为 5 分钟
user = get_cached_user(id)
4. 结构即架构图
代码的组织方式应该反映系统的架构:
# 不清楚的结构
user_repo.py # 2000行,包含用户、订单、支付所有逻辑
# 清楚的结构
user/
repository.py # 用户数据访问
service.py # 用户业务逻辑
validators.py # 用户数据验证
order/
repository.py
service.py
payment/
repository.py
service.py
一眼就能看出系统的模块划分。
检查清单:你的代码是否在沟通
写完代码后,问自己:
如果有任何一项答案是"不能"或"不确定",代码需要改进。
代码审查的真正目的
Code Review 不是为了找茬,而是检查代码是否在有效沟通:
不好的 Code Review 评论:
"这个变量名不符合规范。"
好的 Code Review 评论:
"这个变量名
data太宽泛了,6 个月后我们可能记不清它代表什么。建议改成user_profile_data,这样一眼就知道它存的是用户资料。"
总结
代码的本质是沟通工具:
- 机器能理解任何合法的代码 —— 但人类需要清晰、有意义的代码
- 代码被读的次数远多于被写的次数 —— 为读者优化,而不是为编写速度优化
- 好的代码是自解释的 —— 命名、结构、注释共同传达设计意图
- AI 生成的代码需要人类润色 —— 让它从"机器友好"变成"人类友好"
- 清晰度是第一优先级 —— 性能可以后期优化,但混乱的代码会永久性拖累团队
记住:
你不是在写代码,你是在写一封给未来维护者的信。
这个维护者可能是你的同事,可能是你的继任者,很可能就是6个月后的你自己。
写得清楚一点,未来会感谢你的。

浙公网安备 33010602011771号