当 AI 让代码变成一次性耗材:测试、意图与软件维护的重心迁移

2026-08-20 30 预计阅读时间: 1 分钟
来源: infoq.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

AI 编程带来的变化,不只是写代码更快。更深的一层是:代码正在从人类主要阅读的说明,变成机器和测试共同消费的实现材料。生成速度提高后,许多代码的生命周期会缩短,重写可能比调试更便宜;而测试则越来越像真正的行为文档。

这并不意味着工程质量不再重要,恰恰相反,它要求团队把注意力从“每一行实现是否优雅”转向“系统意图是否清楚、行为是否可验证、替换是否可控”。

可读性仍然重要,但它不再是唯一入口

AI 生成的代码常常足够密集,能运行,却不容易被人快速审查。问题不一定来自语法复杂,而是实现细节太多:异常路径、边界条件、依赖调用和兼容逻辑混在一起,读者很难从代码直接还原设计意图。

当人类无法按同样速度审查全部生成代码时,团队需要把“可理解性”分层:

  • 对外暴露的接口、数据模型和业务规则,必须保持清晰稳定。
  • 关键不变量应该由测试表达,而不是只藏在实现细节中。
  • 低价值、局部性的胶水代码可以更容易被替换。
  • 复杂算法、权限边界和数据迁移仍需要人工深入审查。

这里的重点不是放弃代码审查,而是改变审查对象。审查者不应试图逐字符证明一大段 AI 代码“看起来没问题”,而应检查它是否满足明确的契约,以及契约是否覆盖了真正危险的行为。

测试从回归工具变成行为文档

如果实现可以被快速重写,那么稳定的测试就承担了更大的责任。好的测试不只是验证当前函数返回什么,还要说明系统允许什么、拒绝什么,以及失败时如何表现。

例如,一个订单折扣模块可以先把业务意图写成可运行的测试:

from decimal import Decimal


def calculate_total(subtotal: Decimal, coupon: str | None) -> Decimal:
    """Return the payable amount after applying a valid coupon."""
    if subtotal < 0:
        raise ValueError("subtotal must be non-negative")
    if coupon == "SAVE10":
        return (subtotal * Decimal("0.90")).quantize(Decimal("0.01"))
    return subtotal.quantize(Decimal("0.01"))


def test_coupon_applies_ten_percent_discount():
    assert calculate_total(Decimal("100.00"), "SAVE10") == Decimal("90.00")


def test_unknown_coupon_does_not_change_total():
    assert calculate_total(Decimal("19.99"), "NOPE") == Decimal("19.99")


def test_negative_subtotal_is_rejected():
    try:
        calculate_total(Decimal("-1.00"), None)
        assert False, "expected ValueError"
    except ValueError:
        pass

将代码保存为 test_discount.py 后,可以用下面的命令运行:

python -m pytest -q test_discount.py

这个例子仍然很小,但它明确了三个契约:有效优惠券如何计算、未知优惠券如何处理、非法金额如何拒绝。未来无论是人工重写,还是让 AI 重新生成实现,只要测试和接口契约保持不变,替换的风险就可控。实际项目中可以进一步使用 pytest.raises、参数化测试和属性测试,让边界行为表达得更直接。

从实现耦合转向意图耦合

传统维护通常假设:团队会长期理解并渐进式修改同一套实现。AI 时代可能出现另一种经济模型:先描述目标,生成一版实现,运行测试和生产指标,再在修改成本超过重写成本时替换它。

这会让“意图与实现解耦”成为重要能力。可以把系统拆成三层:

  1. 意图层:业务规则、API 契约、数据约束、可靠性目标和安全边界。
  2. 验证层:单元测试、集成测试、契约测试、静态检查和监控告警。
  3. 实现层:具体的函数、模块、框架配置和 AI 生成的胶水代码。

实现层可以变化,前两层必须有稳定的维护责任。否则,所谓“容易重写”只会变成“每次重写都重新猜测业务规则”。

在服务边界上,契约可以用 OpenAPI 或类似格式固定下来。一个最小的示例:

openapi: 3.0.3
info:
  title: Order API
  version: 1.0.0
paths:
  /orders/{id}:
    get:
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Order found
        '404':
          description: Order does not exist

这份文件不能替代授权、幂等性和错误语义的完整设计,但它比一段未经验证的控制器代码更适合作为跨团队和跨生成周期的约束。

不能把“可丢弃”误读成“无需负责”

代码可替换,不代表系统后果可回收。以下部分不适合因为生成便宜就降低标准:

  • 身份认证、授权和个人数据处理。
  • 金融计算、库存扣减和不可逆的数据迁移。
  • 并发控制、分布式一致性和故障恢复。
  • 影响用户权益的排序、定价或自动决策。

这些领域的风险通常不是“某个函数以后难不难改”,而是错误一旦发生,可能已经写入数据、触发支付或造成合规问题。AI 可以帮助生成候选实现,但必须配合威胁建模、人工审批、审计日志、回滚方案和针对性测试。

团队可以用一份简单的检查表决定哪些代码允许快速替换:

  • 输入、输出和失败语义是否有明确契约?
  • 关键业务规则是否有自动化测试?
  • 是否能在隔离环境中验证生成结果?
  • 失败是否可观测,是否有回滚或重放路径?
  • 这段代码处理的数据和权限是否允许低信任实现?

把工程时间花在更难生成的地方

AI 降低了实现的边际成本,却没有自动解决问题定义、取舍判断和责任归属。开发者更值得投入的工作包括:澄清模糊需求,设计边界和不变量,选择正确的抽象,识别异常后果,以及建立能持续发现偏差的验证系统。

实际采用时,可以从一个边界清楚的小模块开始:先写契约和失败案例,再让 AI 生成实现;把测试、类型检查和运行指标接入流水线;连续几轮修改后,比较修复旧实现和重新生成的成本。这样得到的是基于数据的决策,而不是把“AI 代码都该重写”当成口号。

最终,代码可能越来越像临时构件,但软件工程不会因此消失。工程价值会从记住实现细节,迁移到定义意图、保护边界、验证行为,并在必要时以足够低的成本更换实现。


相关推荐