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万行代码
但依然可维护,为什么?
- 严格的代码审查:每个补丁都被仔细审查
- 模块化:清晰的子系统划分
- 删除旧代码:废弃的驱动会被移除
- 编码规范:统一的风格和约束
教训:
- 复杂度增长不可避免
- 但可以通过纪律和流程管理
- 关键是主动对抗,而非放任
检查清单
定期检查系统复杂度:
如果有超过 3 项答案是"否"或"是",需要重构了。
总结
简单系统天然趋向复杂:
- 需求累积——每个需求都增加复杂度
- 特例增多——真实世界充满边界情况
- 时间腐化——旧代码不会自动改进
- 团队扩张——人多导致风格不一致
- 主动对抗——通过重构、删除、约束来控制
记住:
复杂度像熵一样,自然增长。
不主动对抗复杂度,系统终将无法维护。
最好的代码是昨天删除的代码。

浙公网安备 33010602011771号