1-2-2-代码是系统的自然语言
1.2.2 代码是系统的自然语言
引言
如果代码是沟通工具,那么一个完整的代码库就是一种语言系统。
就像自然语言有词汇、语法、习语一样,代码库也有:
- 词汇:函数名、变量名、类名
- 语法:编程范式、设计模式、代码组织方式
- 习语:团队的编码约定、常见模式
理解"代码是系统的自然语言"这个概念,能帮你:
- 写出与现有代码风格一致的代码
- 快速理解一个新项目
- 识别代码库的"口音"和"方言"
- 避免创造自己的"新语言"导致混乱
代码库的词汇系统
命名的一致性
在同一个代码库中,命名应该遵循统一的"词汇表"。
不一致的词汇:
# 文件1:用户模块
class User:
def get_orders(self): ...
# 文件2:产品模块
class Product:
def fetch_reviews(self): ... # 为什么用 fetch 而不是 get?
# 文件3:订单模块
class Order:
def retrieve_items(self): ... # 为什么用 retrieve?
一致的词汇:
# 统一使用 get_ 前缀表示获取关联数据
class User:
def get_orders(self): ...
class Product:
def get_reviews(self): ...
class Order:
def get_items(self): ...
建立词汇表
每个项目都应该有自己的"词汇表"——明确常用术语的含义:
| 术语 | 含义 | 示例 |
|---|---|---|
get_* |
获取单个对象,不存在时返回 None | get_user(id) |
find_* |
查找多个对象,返回列表 | find_users(filters) |
create_* |
创建新对象 | create_user(data) |
update_* |
更新现有对象 | update_user(id, data) |
delete_* |
删除对象 | delete_user(id) |
is_* |
布尔判断 | is_active(user) |
has_* |
拥有判断 | has_permission(user, perm) |
validate_* |
验证数据,抛出异常或返回错误 | validate_email(email) |
process_* |
处理复杂流程 | process_payment(order) |
一致性的价值:
当你看到 get_product(id),你立即知道:
- 它返回单个产品
- 如果产品不存在,返回
None - 它不会修改任何数据
代码库的语法系统
1. 错误处理的"语法"
不同的项目对错误处理有不同的"语法规则"。
风格 A:异常驱动
def get_user(user_id):
user = db.query(User).filter_by(id=user_id).first()
if not user:
raise UserNotFoundError(f"User {user_id} not found")
return user
# 使用
try:
user = get_user(123)
print(user.name)
except UserNotFoundError:
print("User not found")
风格 B:返回值驱动
def get_user(user_id):
user = db.query(User).filter_by(id=user_id).first()
return user # None if not found
# 使用
user = get_user(123)
if user:
print(user.name)
else:
print("User not found")
两种风格都没错,但在同一个项目中必须统一。
混乱的例子:
# 文件1
def get_user(id):
# 返回 None
return db.get(id)
# 文件2
def get_order(id):
# 抛出异常
order = db.get(id)
if not order:
raise OrderNotFoundError()
return order
# 使用时的困惑
user = get_user(123) # 需要检查 None
order = get_order(456) # 需要捕获异常?还是也返回 None?
2. 参数传递的"语法"
位置参数 vs 关键字参数
# 风格 A:倾向位置参数(短小函数)
def create_user(name, email):
...
create_user("Alice", "alice@example.com")
# 风格 B:倾向关键字参数(复杂函数)
def create_user(*, name, email, role='user', active=True):
...
create_user(name="Alice", email="alice@example.com")
在同一个项目中,相似功能的函数应该使用相同的参数风格。
3. 数据验证的"语法"
在何处验证?
风格 A:在服务层验证
class UserService:
def create_user(self, data):
# 验证在服务层
if not is_valid_email(data['email']):
raise ValidationError("Invalid email")
user = User(**data)
db.save(user)
风格 B:在模型层验证
class User(Model):
def __init__(self, email, **kwargs):
# 验证在模型层
if not is_valid_email(email):
raise ValidationError("Invalid email")
self.email = email
选哪种都可以,但要保持一致。 如果有的验证在服务层,有的在模型层,维护者会困惑。
代码库的习语系统
什么是代码习语
习语(idiom)是指一个项目中反复出现的、约定俗成的模式。
示例习语1:分页查询
# 这个项目的习语
def list_users(page=1, page_size=20):
offset = (page - 1) * page_size
users = db.query(User).limit(page_size).offset(offset).all()
total = db.query(User).count()
return {
'items': users,
'total': total,
'page': page,
'page_size': page_size
}
# 其他地方也遵循相同的模式
def list_orders(page=1, page_size=20):
offset = (page - 1) * page_size
orders = db.query(Order).limit(page_size).offset(offset).all()
total = db.query(Order).count()
return {
'items': orders,
'total': total,
'page': page,
'page_size': page_size
}
当新人看到 list_products(page=2),他会立即知道返回值的结构,因为这是项目的"习语"。
示例习语2:依赖注入
# 这个项目的习语:通过构造函数注入依赖
class UserService:
def __init__(self, user_repo, email_service):
self.user_repo = user_repo
self.email_service = email_service
def create_user(self, data):
user = self.user_repo.create(data)
self.email_service.send_welcome(user)
return user
# 在整个项目中都这样做
class OrderService:
def __init__(self, order_repo, payment_service):
self.order_repo = order_repo
self.payment_service = payment_service
违反习语的代价
案例:
一个项目中,95% 的代码使用这种模式:
result = some_operation()
if result.success:
return result.data
else:
log_error(result.error)
return None
然后新来的开发者写了一段代码:
try:
data = some_operation()
return data
except Exception as e:
log_error(e)
return None
问题:虽然功能正确,但风格不一致:
- 其他人会疑惑:"为什么这里用异常,别的地方用
result.success?" - 如果需要统一改成异常风格,这段代码就成了"先行者",但没有文档说明
- Code Review 时容易引发争论
正确做法:
- 遵循现有习语(即使你不喜欢)
- 如果要改变习语,先讨论并更新团队规范,再批量修改
代码方言:不同项目的"口音"
不同的项目、团队、公司会形成自己的"代码方言"。
Django 的方言
# Django 风格:基于类的视图
from django.views import View
class UserListView(View):
def get(self, request):
users = User.objects.all()
return JsonResponse({'users': users})
Flask 的方言
# Flask 风格:基于函数的路由
from flask import Flask, jsonify
app = Flask(__name__)
@app.route('/users')
def list_users():
users = User.query.all()
return jsonify({'users': users})
React 的方言
// React 风格:组件化
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetchUsers().then(setUsers);
}, []);
return (
<div>
{users.map(user => <UserCard key={user.id} user={user} />)}
</div>
);
}
每个框架都有自己的"方言",学习框架就是学习这种方言。
代码库演化:语言的变迁
代码库会随着时间演化,就像自然语言会演化一样。
古老代码 vs 现代代码
5 年前的代码(jQuery 时代):
$(document).ready(function() {
$('#load-users').click(function() {
$.ajax({
url: '/api/users',
success: function(data) {
var html = '';
data.forEach(function(user) {
html += '<div>' + user.name + '</div>';
});
$('#user-list').html(html);
}
});
});
});
现代代码(React/Vue 时代):
function UserList() {
const [users, setUsers] = useState([]);
const loadUsers = async () => {
const response = await fetch('/api/users');
const data = await response.json();
setUsers(data);
};
return (
<div>
<button onClick={loadUsers}>Load Users</button>
<div>
{users.map(user => <div key={user.id}>{user.name}</div>)}
</div>
</div>
);
}
在同一个项目中,两种"方言"可能共存,这会导致混乱。
语言迁移策略
错误做法:
让新旧代码风格混杂,没有明确的迁移计划。
// 文件1:旧风格(jQuery)
$('#submit').click(function() { ... });
// 文件2:新风格(React)
<button onClick={handleSubmit}>Submit</button>
// 文件3:又回到旧风格?
$('#cancel').click(function() { ... });
正确做法:
- 明确迁移方向:写在团队文档里
- 新代码全部用新风格
- 旧代码逐步重构,或至少注明"遗留代码"
# legacy/user_service.py
# 这是遗留代码,使用旧的错误处理风格
# 新代码请参考 services/user_service.py
def get_user(id):
# 返回 None 的旧风格
return db.get(id)
对使用 AI 的程序员的建议
AI 不了解你的项目的"语言系统",它会用自己的"方言"生成代码。
问题示例
你的项目的习语:
# 你的项目统一用这种风格
def list_users(filters=None):
query = User.query
if filters:
query = query.filter_by(**filters)
return query.all()
AI 生成的代码:
# AI 可能生成这种风格
def get_all_products(filter_dict={}):
if filter_dict:
products = Product.objects.filter(**filter_dict).all()
else:
products = Product.objects.all()
return list(products)
不一致之处:
- 函数名:
list_vsget_all_ - 参数名:
filtersvsfilter_dict - 默认值:
Nonevs{}(后者是反模式) - 查询方式:
.query.filter_by()vs.objects.filter()
如何让 AI 遵循你的"语言"
方法1:在提示词中明确风格
请帮我写一个获取产品列表的函数,要求:
- 函数名使用 list_* 格式(如 list_products)
- 过滤参数命名为 filters,默认值为 None
- 使用 Product.query.filter_by() 进行查询
- 返回查询结果,不需要转换成 list
方法2:提供示例代码
这是我们项目中获取用户列表的代码:
[粘贴你的代码]
请按照相同的风格写一个获取产品列表的函数。
方法3:生成后手动改写
AI 生成代码后,检查并改写,使其符合项目习语。
实践原则
1. 新加入项目时:先学习项目的"语言"
- 阅读几个核心模块的代码
- 识别常见的命名模式、错误处理方式、代码组织结构
- 找到"词汇表"(如果有的话)或自己总结一份
2. 写新代码时:遵循现有的"语法"和"习语"
- 在写代码之前,先找一个类似功能的现有代码作为参考
- 复制-修改比从零开始写更容易保持一致性
示例:
需要写一个 create_product() 函数,先找到 create_user() 函数,参考它的:
- 参数风格
- 验证方式
- 错误处理
- 返回值格式
3. 重构时:逐步统一"方言"
如果发现项目中有多种风格混杂:
- 不要一次性改变所有代码(风险太大)
- 先统一新代码的风格
- 在重构旧代码时,顺便统一风格
- 更新团队文档,明确"标准方言"
4. Code Review 时:检查语言一致性
除了逻辑正确性,还要检查:
- 命名是否符合项目词汇表
- 错误处理是否符合项目习惯
- 代码结构是否符合项目模式
好的 Code Review 评论:
"我们项目中的删除操作统一命名为
delete_*,建议把remove_user改成delete_user,这样和delete_order、delete_product保持一致。"
检查清单:你的代码是否符合项目的"语言"
完成一段代码后,检查:
真实案例:代码风格的价值
Airbnb JavaScript 风格指南
Airbnb 发布了详细的 JavaScript 风格指南,并在全公司执行。
为什么?
"当你在一个陌生的代码库中,如果代码风格一致,你会觉得这是你熟悉的地方。你能立即理解代码在做什么,而不需要先适应不同的风格。"
Google 的代码风格指南
Google 为每种语言都制定了详细的代码风格指南(C++, Java, Python, Go...)。
为什么?
"当数千名工程师在同一个代码库工作时,一致性比个人偏好更重要。统一的风格降低了理解成本,提高了协作效率。"
总结
代码是系统的自然语言:
- 每个项目都有自己的"方言" —— 词汇、语法、习语
- 一致性是可读性的基础 —— 统一的命名、错误处理、代码组织
- 学习项目的"语言"是融入团队的第一步 —— 观察、模仿、遵循
- AI 生成的代码需要"翻译"成项目的方言 —— 改写成符合项目风格的代码
- 代码风格不是个人喜好问题 —— 而是团队协作的工程问题
记住:
在罗马,做罗马人做的事。
在一个项目里,写这个项目风格的代码。
不要创造自己的"新语言",除非你准备好重写整个项目。

浙公网安备 33010602011771号