2-2-2-简单系统天然趋向复杂

2.2.2 简单系统天然趋向复杂

引言

你刚接手一个项目时:

  • 500 行代码,结构清晰
  • 3 个模块,职责分明
  • 一眼就能看懂整个系统

6 个月后:

  • 5000 行代码,分散在 50 个文件
  • 模块边界模糊,互相依赖
  • 没有人能完整理解整个系统

再过 1 年:

  • 代码量翻倍
  • 添加新功能需要修改 10 个文件
  • 每次修改都可能引入新 bug

没有人故意让系统变复杂,但系统自然会趋向复杂。

这不是开发者的错,而是软件系统的本质。

复杂度的来源

来源1:需求的累积

# 第1周:最简单的需求
class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email

# 第2周:需要密码
class User:
    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password_hash = hash_password(password)

# 第1个月:需要权限
class User:
    def __init__(self, username, email, password, role='user'):
        self.username = username
        self.email = email
        self.password_hash = hash_password(password)
        self.role = role
        self.permissions = self._get_permissions_for_role(role)

# 第3个月:需要多种登录方式
class User:
    def __init__(self, username, email, password=None, oauth_provider=None,
                 oauth_id=None, role='user', phone=None, is_verified=False):
        self.username = username
        self.email = email
        self.password_hash = hash_password(password) if password else None
        self.oauth_provider = oauth_provider
        self.oauth_id = oauth_id
        self.role = role
        self.permissions = self._get_permissions_for_role(role)
        self.phone = phone
        self.is_verified = is_verified
        self.created_at = datetime.now()
        self.last_login = None

# 6个月后:User 类已经有 20 个字段,10 个方法
# 每个需求都合理,但累积起来变得复杂

规律:每个新需求都增加复杂度,没有需求会减少复杂度。

来源2:边界情况

# 最初:简单的逻辑
def calculate_price(quantity, unit_price):
    return quantity * unit_price

# 需求1:满100减10
def calculate_price(quantity, unit_price):
    total = quantity * unit_price
    if total >= 100:
        total -= 10
    return total

# 需求2:VIP用户九折
def calculate_price(quantity, unit_price, is_vip=False):
    total = quantity * unit_price
    if is_vip:
        total *= 0.9
    if total >= 100:
        total -= 10
    return total

# 需求3:某些商品不参与满减
def calculate_price(quantity, unit_price, is_vip=False, is_discountable=True):
    total = quantity * unit_price
    if is_vip:
        total *= 0.9
    if is_discountable and total >= 100:
        total -= 10
    return total

# 需求4:不同商品类别有不同折扣
def calculate_price(quantity, unit_price, is_vip=False, is_discountable=True,
                   category=None, promotion_code=None):
    total = quantity * unit_price

    # VIP折扣
    if is_vip:
        total *= 0.9

    # 分类折扣
    if category == 'electronics':
        total *= 0.95
    elif category == 'books':
        total *= 0.85

    # 满减
    if is_discountable and total >= 100:
        total -= 10

    # 优惠码
    if promotion_code:
        discount = get_promotion_discount(promotion_code)
        total -= discount

    return max(0, total)  # 不能为负

# 问题:函数变得复杂,边界情况互相影响

规律:真实世界充满特例,每个特例都增加复杂度。

来源3:时间的累积

# 2020年:原始实现
def send_notification(user, message):
    send_email(user.email, message)

# 2021年:添加短信
def send_notification(user, message):
    send_email(user.email, message)
    if user.phone:
        send_sms(user.phone, message)

# 2022年:添加推送
def send_notification(user, message):
    send_email(user.email, message)
    if user.phone:
        send_sms(user.phone, message)
    if user.push_token:
        send_push(user.push_token, message)

# 2023年:用户偏好设置
def send_notification(user, message):
    if user.email_enabled:
        send_email(user.email, message)
    if user.sms_enabled and user.phone:
        send_sms(user.phone, message)
    if user.push_enabled and user.push_token:
        send_push(user.push_token, message)

# 2024年:某些通知是强制的
def send_notification(user, message, force=False):
    if force or user.email_enabled:
        send_email(user.email, message)
    if force or (user.sms_enabled and user.phone):
        send_sms(user.phone, message)
    if force or (user.push_enabled and user.push_token):
        send_push(user.push_token, message)

# 问题:没有人重构,只是不断添加
# 代码变成了历史的堆积

规律:时间会让代码腐化,旧代码不会自动改进。

来源4:团队的扩张

# 1人团队:风格统一
def get_user(user_id):
    return db.query(User).get(user_id)

def get_order(order_id):
    return db.query(Order).get(order_id)

# 3人团队:开始出现差异
def get_user(user_id):
    return db.query(User).get(user_id)

def fetch_order(order_id):  # 有人喜欢用 fetch
    return db.query(Order).filter_by(id=order_id).first()

def retrieve_product(product_id):  # 有人喜欢用 retrieve
    result = db.session.execute(
        "SELECT * FROM products WHERE id = :id",
        {'id': product_id}
    )
    return result.fetchone()

# 10人团队:风格混乱
# - 3种命名风格(get/fetch/retrieve)
# - 4种查询方式(ORM/原生SQL/query builder/raw)
# - 5种错误处理方式
# 没有人故意破坏一致性,但复杂度自然增长

规律:团队越大,复杂度增长越快。

复杂度的表现

表现1:认知负担增加

# 简单系统:一眼看懂
def create_order(user_id, items):
    order = Order(user_id=user_id, items=items)
    db.save(order)
    return order

# 复杂系统:需要理解很多上下文
def create_order(user_id, items, payment_method=None, shipping_address=None,
                promotion_code=None, is_gift=False, gift_message=None):
    # 1. 验证用户
    user = user_service.get_user(user_id)
    if not user.is_active:
        raise UserNotActiveError()

    # 2. 验证库存
    for item in items:
        if not inventory_service.check_stock(item.product_id, item.quantity):
            raise OutOfStockError(item.product_id)

    # 3. 计算价格
    pricing = pricing_service.calculate(
        items=items,
        user=user,
        promotion_code=promotion_code
    )

    # 4. 验证支付方式
    if payment_method:
        if not payment_service.validate_method(user, payment_method):
            raise InvalidPaymentMethodError()

    # 5. 创建订单
    order = Order(
        user_id=user_id,
        items=items,
        total=pricing.total,
        tax=pricing.tax,
        shipping=pricing.shipping,
        discount=pricing.discount,
        payment_method=payment_method,
        shipping_address=shipping_address or user.default_address,
        is_gift=is_gift,
        gift_message=gift_message if is_gift else None
    )

    # 6. 扣减库存
    inventory_service.reserve(items)

    # 7. 保存订单
    order_repository.save(order)

    # 8. 发送通知
    notification_service.send_order_created(order)

    # 9. 记录分析事件
    analytics_service.track('order_created', order)

    return order

# 现在需要理解 6 个服务的交互
# 每个步骤可能失败,需要回滚

表现2:修改的涉及面扩大

# 简单系统:修改局部
# "给用户添加一个字段"
# 修改 1 个文件

# 复杂系统:修改全局
# "给用户添加一个字段"
# 需要修改:
# - models/user.py
# - schemas/user_schema.py
# - api/user_api.py
# - services/user_service.py
# - repositories/user_repository.py
# - migrations/xxx_add_user_field.py
# - tests/test_user.py
# - docs/api.md

# 一个简单的改动影响 8 个文件

表现3:依赖关系复杂

# 简单系统:线性依赖
# main.py → user.py → database.py

# 复杂系统:网状依赖
# user_service → user_repository
#             ↓ order_service
#             ↓ notification_service
#             ↓ payment_service

# order_service → order_repository
#               ↓ user_service  # 循环依赖!
#               ↓ inventory_service
#               ↓ pricing_service

# 改动一个模块可能影响 10 个其他模块

对抗复杂度的策略

策略1:定期重构

# 不要等到无法维护才重构
# 每个迭代都分配时间重构

# 示例:简化复杂函数
def create_order(user_id, items, **options):
    # 提取验证逻辑
    self._validate_order(user_id, items, options)

    # 提取计算逻辑
    pricing = self._calculate_pricing(user_id, items, options)

    # 提取订单创建
    order = self._build_order(user_id, items, pricing, options)

    # 提取副作用
    self._handle_order_side_effects(order)

    return order

# 每个私有方法都清晰、可测试

策略2:删除而非添加

# 每次添加新代码时,问自己:
# "我能删除什么旧代码?"

# 示例:删除未使用的功能
# 使用工具检测未使用的代码
# pylint --disable=all --enable=unused-import,unused-variable

# 删除:
# - 未使用的函数
# - 已废弃的功能
# - 重复的逻辑
# - 临时的 workaround

# 代码量不增长 = 复杂度不增长

策略3:限制系统规模

# 设定硬性限制

# 文件大小限制
# 单个文件不超过 500 行

# 函数复杂度限制
# McCabe 复杂度不超过 10

# 依赖深度限制
# 依赖链不超过 3 层

# 超过限制就必须重构
# 强制对抗复杂度增长

策略4:拆分系统

# 当单体系统太复杂时,拆分

# 从
monolith/
  ├── 50000行代码
  ├── 200个模块
  └── 无人能完整理解

# 到
user_service/      # 5000行,专注用户管理
order_service/     # 6000行,专注订单处理
payment_service/   # 3000行,专注支付

# 每个服务都简单、可理解

策略5:建立约束

# 通过规则限制复杂度

# 约束1:禁止循环依赖
# 使用 importchecker 工具检测

# 约束2:限制参数数量
def create_order(request: CreateOrderRequest):
    # 用对象封装参数,而不是无限添加参数
    ...

# 约束3:强制分层
# domain层不能依赖infrastructure层
# application层不能直接访问数据库

# 约束4:限制文件数量
# 单个目录不超过 10 个文件
# 超过就拆分子目录

复杂度的测量

指标1:圈复杂度

# 简单函数:圈复杂度 = 1
def add(a, b):
    return a + b

# 中等复杂:圈复杂度 = 3
def calculate_discount(total, is_vip):
    if is_vip:
        if total > 100:
            return 20
        else:
            return 10
    return 0

# 高复杂度:圈复杂度 = 10+
def process_order(...):
    if condition1:
        if condition2:
            if condition3:
                # 嵌套太深
                ...

# 目标:圈复杂度 < 10

指标2:耦合度

# 低耦合:依赖少
class OrderService:
    def __init__(self, repository):
        self.repo = repository  # 只依赖 1 个

# 高耦合:依赖多
class OrderService:
    def __init__(self, user_service, inventory_service,
                 payment_service, notification_service,
                 analytics_service, logger, cache):
        # 依赖 7 个,太多了
        ...

# 目标:构造函数参数 < 3

指标3:代码行数

# 跟踪代码行数增长
# 2024-01: 10000行
# 2024-02: 12000行 (+20%)
# 2024-03: 15000行 (+25%)

# 如果增长过快,检查是否:
# - 有重复代码可以提取
# - 有冗余功能可以删除
# - 需要拆分模块

对使用 AI 的程序员的建议

AI 的常见问题

问题1:只添加不删除

# AI 总是添加新代码
# "添加一个功能"

# 但AI很少建议:
# - 删除重复的代码
# - 移除未使用的功能
# - 简化复杂的逻辑

# 结果:代码越来越多,越来越复杂

问题2:不考虑累积效应

# AI 单次生成的代码可能合理
# 但 10 次累积后,系统变得混乱

# 示例
# 第1次:"添加邮件通知"
# 第2次:"添加短信通知"
# 第3次:"添加推送通知"
# ...
# 第10次:通知逻辑散落在 20 个文件

# AI 不会主动说:"应该重构通知系统了"

如何改进

提示词策略

❌ 不好的提示:
"添加一个VIP用户折扣功能"

✅ 好的提示:
"我要添加VIP用户折扣功能。
当前系统已有普通折扣和促销码。
请:
1. 检查是否有重复逻辑可以合并
2. 如果折扣逻辑变复杂,建议重构方案
3. 确保新代码不增加系统复杂度
4. 指出可以删除或简化的旧代码"

定期审查

真实案例

案例:Linux 内核的复杂度管理

1991年:10000行代码
2024年:3000万行代码

但依然可维护,为什么?

  1. 严格的代码审查:每个补丁都被仔细审查
  2. 模块化:清晰的子系统划分
  3. 删除旧代码:废弃的驱动会被移除
  4. 编码规范:统一的风格和约束

教训

  • 复杂度增长不可避免
  • 但可以通过纪律和流程管理
  • 关键是主动对抗,而非放任

检查清单

定期检查系统复杂度:

如果有超过 3 项答案是"否"或"是",需要重构了。

总结

简单系统天然趋向复杂:

  1. 需求累积——每个需求都增加复杂度
  2. 特例增多——真实世界充满边界情况
  3. 时间腐化——旧代码不会自动改进
  4. 团队扩张——人多导致风格不一致
  5. 主动对抗——通过重构、删除、约束来控制

记住:

复杂度像熵一样,自然增长。

不主动对抗复杂度,系统终将无法维护。

最好的代码是昨天删除的代码。

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