1-2-2-代码是系统的自然语言

1.2.2 代码是系统的自然语言

引言

如果代码是沟通工具,那么一个完整的代码库就是一种语言系统

就像自然语言有词汇、语法、习语一样,代码库也有:

  • 词汇:函数名、变量名、类名
  • 语法:编程范式、设计模式、代码组织方式
  • 习语:团队的编码约定、常见模式

理解"代码是系统的自然语言"这个概念,能帮你:

  1. 写出与现有代码风格一致的代码
  2. 快速理解一个新项目
  3. 识别代码库的"口音"和"方言"
  4. 避免创造自己的"新语言"导致混乱

代码库的词汇系统

命名的一致性

在同一个代码库中,命名应该遵循统一的"词汇表"。

不一致的词汇

# 文件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 时容易引发争论

正确做法

  1. 遵循现有习语(即使你不喜欢)
  2. 如果要改变习语,先讨论并更新团队规范,再批量修改

代码方言:不同项目的"口音"

不同的项目、团队、公司会形成自己的"代码方言"。

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() { ... });

正确做法

  1. 明确迁移方向:写在团队文档里
  2. 新代码全部用新风格
  3. 旧代码逐步重构,或至少注明"遗留代码"
# 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_ vs get_all_
  • 参数名:filters vs filter_dict
  • 默认值:None vs {}(后者是反模式)
  • 查询方式:.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_orderdelete_product 保持一致。"

检查清单:你的代码是否符合项目的"语言"

完成一段代码后,检查:

真实案例:代码风格的价值

Airbnb JavaScript 风格指南

Airbnb 发布了详细的 JavaScript 风格指南,并在全公司执行。

为什么?

"当你在一个陌生的代码库中,如果代码风格一致,你会觉得这是你熟悉的地方。你能立即理解代码在做什么,而不需要先适应不同的风格。"

Google 的代码风格指南

Google 为每种语言都制定了详细的代码风格指南(C++, Java, Python, Go...)。

为什么?

"当数千名工程师在同一个代码库工作时,一致性比个人偏好更重要。统一的风格降低了理解成本,提高了协作效率。"

总结

代码是系统的自然语言:

  1. 每个项目都有自己的"方言" —— 词汇、语法、习语
  2. 一致性是可读性的基础 —— 统一的命名、错误处理、代码组织
  3. 学习项目的"语言"是融入团队的第一步 —— 观察、模仿、遵循
  4. AI 生成的代码需要"翻译"成项目的方言 —— 改写成符合项目风格的代码
  5. 代码风格不是个人喜好问题 —— 而是团队协作的工程问题

记住:

在罗马,做罗马人做的事。

在一个项目里,写这个项目风格的代码。

不要创造自己的"新语言",除非你准备好重写整个项目。

posted @ 2025-11-29 21:54  Jack_Q  阅读(1)  评论(0)    收藏  举报