Skip to content

Maintainability(可维护性)—— 代码的长期可持续性 ​

解决什么问题:代码上线 6 个月后没人能改 = 维护性破产—— 可维护性不是"代码写得漂亮",是"长期可持续"的工程能力—— 新人能看懂、能改、能扩展、能删除 = 业务演化阻力小 = 可持续性高。 可维护性 = 业务能持续演化的前提。

它是什么 ​

Maintainability(可维护性) 是 L2 设计层的核心质量属性——指代码在长期演化过程中的可修改性。

可维护性的 3 个维度:

维度含义衡量
可理解性(Understandability)新人能快速读懂代码首次 PR 改通时间
可修改性(Modifiability)改一处不波及其他每次修改影响范围
可测试性(Testability)容易写测试、容易验证测试覆盖率 + 测试速度

可维护性的对立面 = 技术债(Tech Debt):

  • 越难改的代码 = 技术债越多
  • 越难理解的代码 = 技术债越多
  • 越难测试的代码 = 技术债越多

SE@G Ch 9(Winters)原话: "Code is a liability. The more code you have, the more places there are for bugs to hide."

"Maintainability is not about how easy it is to write code; it's about how easy it is to keep code working over time."

"Your project is sustainable if, for the expected life span of your software, you are capable of reacting to whatever valuable change comes along."

V2 视角(V2.7 B7 段 4 演化 + B4 段 5 抽象 + B5 段 4 反馈): 可维护性 = 代码 = 负债 的反向——负债越少 = 可维护性越高。 代码增删成本 = 业务演化阻力。 可维护性 = 业务演化的"摩擦系数"——摩擦低 = 演化快 = 业务活。

关键属性 ​

属性值/含义
可读性代码风格一致 + 命名清晰 + 注释说明"为什么"
小而专注每个模块/类/函数职责单一
测试覆盖关键路径单测 + 集成测试
依赖清晰依赖有版本、声明、约束
文档同步文档 = 代码生成(OpenAPI / Schema / ADR)
可观测内部状态可查询(日志/指标/链路)
可回滚每个变更可单独回滚
反模式巨型类 / 深度嵌套 / 长函数 / 隐式依赖 / 无测试
判错标志新人首次改通 PR > 1 周 / 改一处引发多处回归 / 无测试覆盖关键路径

怎么用 ​

场景 1:可读性——代码是写给人看的 ​

python
# ❌ 难以理解
def f(x, y):
    z = x * 0.1
    if y > 1000 and z > 50:
        return True
    return False

# ✅ 容易理解
def is_high_value_order_with_discount(order_amount, discount_threshold=1000):
    """判断是否大额订单且折扣大于 50"""
    DISCOUNT_RATE = 0.1
    discount = order_amount * DISCOUNT_RATE
    is_large_order = order_amount > discount_threshold
    has_significant_discount = discount > 50
    return is_large_order and has_significant_discount

可读性的 5 个实践:

  1. 命名即注释——函数名 / 变量名 / 类名直接说明"做什么"
  2. 注释说明"为什么"——不是"做什么"(代码自己会说"做什么")
  3. 风格统一——Style Guide + Lint + Formatter 自动化
  4. 函数短小——单函数不超过 50 行(典型上限)
  5. 嵌套浅显——不超过 3 层嵌套(用 early return / 提取函数)

场景 2:小而专注——单一职责原则 ​

python
# ❌ 大类(God Class)—— 多个职责
class Order:
    def create(self): ...
    def cancel(self): ...
    def refund(self): ...
    def calculate_tax(self): ...
    def send_email(self): ...        # 不是订单的职责
    def generate_report(self): ...    # 不是订单的职责
    def sync_to_erp(self): ...       # 不是订单的职责

# ✅ 小类——单一职责
class Order:  # 订单的领域逻辑
    def create(self): ...
    def cancel(self): ...
    def refund(self): ...

class TaxCalculator:  # 税务计算
    def calculate(self, order): ...

class OrderNotifier:  # 订单通知
    def send(self, order, template): ...

class OrderReporter:  # 订单报表
    def generate(self, period): ...

class OrderErpSync:  # ERP 同步
    def sync(self, order): ...

单一职责的判断标准:

  • "改变这个类的理由有几个?"——多个理由 = 多个职责 = 应该拆
  • "这个类能被一句话说清楚吗?"——不能 = 应该拆
  • "这个类依赖多少其他模块?"——依赖过多 = 应该拆

场景 3:测试覆盖——可维护性的安全网 ​

python
# 测试金字塔
unit_tests:        70%   - 单函数 / 单类(毫秒级)
integration_tests: 20%   - 模块协作(秒级)
e2e_tests:         10%   - 端到端流程(分钟级)

# 单测示例
def test_order_can_be_paid_when_pending():
    order = Order.create(customer_id="cus-123", items=[...])
    # 不需要数据库 / 不需要网络 / 毫秒级
    order.pay(amount=Money(100, "CNY"))
    assert order.status == OrderStatus.PAID

def test_order_cannot_be_paid_twice():
    order = Order.create(...)
    order.pay(amount=Money(100, "CNY"))
    with pytest.raises(OrderNotPayable):
        order.pay(amount=Money(50, "CNY"))  # 第二次支付应该失败

测试覆盖的 4 个反模式:

  1. 覆盖率至上——100% 覆盖率但都是无意义断言 = 假象
  2. 只测 happy path——边界 / 异常 / 并发没测 = 实际未覆盖
  3. E2E 测试过多——分钟级测试 = CI 跑不动
  4. 测试依赖真实环境——测数据库 / 测网络 = 不可重复

场景 4:依赖清晰——SE@G Ch 21 ​

python
# requirements.txt 显式声明依赖
fastapi==0.110.0      # 主版本固定(不自动升)
pydantic==2.6.0       # 主版本固定
sqlalchemy==2.0.0
pytest==8.0.0

# 测试环境 vs 生产环境分离
requirements.txt          # 生产依赖
requirements-dev.txt      # 开发依赖(pytest / black / mypy)

依赖的 4 个原则(SE@G Ch 21):

  1. One-Version Rule——统一版本(减少协调)
  2. 显式声明——所有依赖在 manifest 里(不是隐式)
  3. 可升级——升级路径明确 + 定期升级
  4. 可审计——依赖有安全审计 / License 检查

场景 5:可观测性 = 内部状态可见 ​

python
class OrderService:
    def create_order(self, cmd):
        # 日志:关键业务事件
        logger.info("order.create.start", extra={
            "customer_id": cmd.customer_id,
            "item_count": len(cmd.items),
        })
        
        try:
            order = Order.create(cmd)
            order_repo.save(order)
            
            # 指标:业务指标
            metrics.counter("order.created").inc()
            metrics.histogram("order.amount").observe(order.total_amount.value)
            
            # 追踪:跨服务调用链
            tracer.span("order.create").set_attribute("order.id", order.id)
            
            return order
        except Exception as e:
            # 错误日志:包含上下文(不只 stack trace)
            logger.error("order.create.failed", extra={
                "customer_id": cmd.customer_id,
                "error": str(e),
            })
            raise

V2 B5 反馈视角——可观测性 = 反馈环的"测量"环节。 没有可观测性的代码 = 反馈环不完整 = 失控。

场景 6:可回滚 = 每个变更可独立回退 ​

python
# 数据库迁移(向前 + 向后兼容)
def migrate_v1_to_v2(conn):
    # 向前:加新字段(兼容,老代码读不到会忽略)
    conn.execute("ALTER TABLE orders ADD COLUMN notes TEXT")
    
    # 不删除旧字段(兼容,老代码还在用)
    # 等所有调用方切到 v2 后再删

def migrate_v2_to_v1(conn):  # 回退
    conn.execute("ALTER TABLE orders DROP COLUMN notes")

可回滚的 4 个层面:

  1. 代码可回滚——Git revert / feature flag
  2. 数据库可回滚——migration 双向兼容
  3. 配置可回滚——配置中心版本化
  4. 依赖可回滚——锁定版本 + 备份

常见误区 ​

  1. "可维护性 = 文档多"——错。文档多 ≠ 可维护。文档与代码脱节 = 比无文档更糟。文档要靠代码生成
  2. "可维护性 = 注释多"——错。注释解释"做什么"= 重复代码。注释要解释"为什么"
  3. "可维护性 = 类多"——错。类多 ≠ 易维护。过度抽象 = 难懂 = 难维护
  4. "可维护性 = 测试覆盖率 100%"——错。无意义断言 = 假覆盖。关键路径覆盖 > 覆盖率
  5. "可维护性 = 重构"——半对。重构是手段,可维护性是结果。盲目重构 = 增加负债
  6. "可维护性 = 用最新技术"——错。新技术 = 学习成本 + 升级成本。稳定技术 > 新技术
  7. "可维护性 = 单人理解"——错。单人能理解但别人不能 = 维护单点故障。可维护性 = 团队都能理解

关键洞察 ​

可维护性不是"代码漂亮",是"长期可持续"——代码能在业务演化中持续被改、被读、被扩展。

SE@G Ch 1:"Software Engineering is programming integrated over time." 可维护性 = 编程 × 时间的工程能力。

V2 B4 抽象的反向——抽象要减少代码总量,不增加代码量。 过度抽象 = 增加间接层 = 增加理解成本 = 降低可维护性。

V2 B7 演化的前提——可维护性 = 演化的摩擦系数。 摩擦高 = 业务演不动 = 业务死。 摩擦低 = 业务能快速响应 = 业务活。

判断可维护性的 5 个指标:

  1. 新人首次改通 PR 的时间(越短越好)
  2. 单次修改影响范围(越小越好)
  3. 关键路径测试覆盖率(越高越好,但覆盖率不是目的)
  4. 技术债清单 + 偿还节奏(显式记录 > 隐性堆积)
  5. 团队对"代码是否能改"的集体信心(质性指标)

可维护性的核心 = 让代码能在"明天"被改,而不是只能"今天"被改。

相关链接 ​

从名家方法论与工程化思路中蒸馏出自己的工程体系。