Skip to content
All notes

5 软件工程的来龙去脉:弥合意图与实现的 gap

软件工程从诞生起就在弥合意图、规格和实现之间的 gap;AI 让实现变便宜,意图和验收变得更重要。

5 软件工程的来龙去脉:弥合意图与实现的 gap

我的理解

  • first principle:把问题拆到最基本、不可再简化的事实,再从这些事实推导解决方案。
  • 软件工程要解决的是 intent、spec、implementation 之间的 gap。
  • 历史上的方法,如瀑布、DAG 视角下的 BFS 与 DFS、形式化语言、UML,要放到 AI 时代重新看定义和取舍:哪些保留,哪些更替。UML 已经死了,至少我现在不会用。

意图、规格与实现

软件工程要弥合的,是三层之间的 gap:

  • intent:想要什么。
  • spec:架构、接口、约束。
  • implementation:实际运行的代码。

需求出自人的设想,提出需求的人往往也没有想清楚边界情况。以选课系统的先修课为例:它是硬约束还是软约束?谁有权特批?学生临近毕业、只差这一门时,学校通常会破例。

intent、spec 与 implementation 之间的 gap

这些 gap 无法靠事先想清楚来消除,原因在于下面三条基本事实。

三条基本事实

  • 测试之前,程序无法运行:设计阶段只有文档,缺乏强约束,偏差要到测试才暴露。例如做 OJ 题时先选定了错误的算法,写完运行样例,才发现整个思路不成立。
  • 不会发生的事终会发生:Hamilton 默认任何假设都可能被违反。阿波罗 11 号下降时触发报警,软件依靠优先级调度与恢复机制,保证下降控制继续运行。
  • 每一步翻译都会出错:从 spec 到代码会出错,不崩溃、不泄漏资源这类隐含的 spec 也会遗漏。例如为 Eiffel 的取款函数写前置条件时,才会考虑余额不足如何处理。

gap 既然无法消除,历史上的方法便沿两条路线去收窄它。

两条路线

  • 提前反馈:pilot、MVP、敏捷、TDD、QuickCheck(描述性质,自动生成用例寻找反例)。
  • 确定语义:自然语言 → checklist → UML 与 DSL → 编程语言加契约 → 形式化证明(Dafny、Verus)。

提前反馈与确定语义的两条路线

traceability 把依赖关系显式记录下来,防御性编程默认前提会被违反,二者是两条路线的补充。评估一种新工具时,可以先问:它属于提前反馈,还是确定语义?

瀑布:返工沿依赖传递

提前反馈这条路线,源自对瀑布模型的反思。Royce 在 1970 年的论文中提出瀑布,本意是批判它。将需求、设计、实现之间的关联视为 DAG,测试失败后须向上追溯,依赖错误设计的实现全部作废;问题发现得越晚,代价越大。

瀑布依赖关系与测试失败后的返工

  • 瀑布即 BFS:逐层铺开,最后测试。
  • MVP 与敏捷即 DFS:先打通优先级最高的一条依赖,每一步都获得反馈。
  • Do it twice:先构建一个准备丢弃的 pilot。丢弃的是代码;需求随之修正,意图保持不变。过去代码昂贵,至多做两遍;如今代码廉价,可以反复迭代。

瀑布 BFS 与 MVP、敏捷 DFS 的对比

两条路线的取舍,始终受制于写代码的成本。AI 改变的正是这一成本。

AI 时代的取舍

讲师认为:LLM 解决了人与人难以对齐、checklist 难以检查的问题,剩下的只有「没有想清楚、没有说清楚」。

方法原本解决的问题AI 时代
UML 等中间语言编码前让人与人对齐不再需要,AI 可在自然语言与代码之间直接翻译
人工 checklist防止遗漏检查交给 AI,与客户确认仍由人负责
MVP、do it twice尽早获得反馈更重要,也更廉价
TDD、契约、断言定义何为正确更重要:AI 负责实现,人负责验收
traceability定位依赖更重要:AI 修改快,漂移也快
任务划分分工不变:leader 须先划分任务,不能交由团队自组织

从 spec 到实现几乎不再有成本,intent 到 spec 的定义与结果的验证因此更有价值。AI 同样让 BFS 变得廉价,可以一次生成整套系统;但 intent 一旦有误,成果仍会全部作废,坚持 DFS 依然依赖人的判断。

人的职责

  • 决定哪些定死,哪些留白:前述所有方法,归根到底都在回答这一问题。
  • 给出可验收的结果,过程交给 Agent:任务划分是否合理,取决于每一部分能否写出验收标准。

漂移:以验收为源头

AI 修改代码的速度远超文档与测试的更新。对策是以 spec 和验收为源头,代码可随时重新生成:

  • 验收写成测试与断言,漂移在运行测试时自动暴露,不依赖人工阅读文档。
  • 修改代码时,在同一次提交中同步更新文档与测试,并写入 AGENTS.md 作为规则。
  • 小步提交,每步运行测试,将漂移限制在单步之内。
  • 由 Agent 定期比对文档与代码,报告不一致之处,思路同第 4 讲的 repo.db。

附录:读一手资料

教科书对所有读者做同一种有损压缩。借助 AI,可以直接阅读原文,压缩成适合自己的版本。

来源:生成式软件工程(NJU)第 5 讲