代码仓库从来都不是全部真相

代码展示的是已经存在的东西。真正的原因散落在规格、事故、数据和决策之中。

代码仓库从来都不是全部真相

在一个功能上线六个月后再打开它的代码仓库,你仍然能还原出很多东西。你可以追踪架构,检查 schema,阅读测试,并准确看到哪些代码行发生了变化。

但你通常无法还原的,是让这些代码行变得必要的那场对话。

代码不会告诉你,某条校验规则之所以存在,是因为有个客户会发送格式错误的导出文件。它不会解释,一个看起来奇怪的重试策略其实是在防止下游系统里出现重复交易。它也不会显示,更干净的界面方案是在无障碍测试后被否决的,或者某个服务边界反映的是合同限制,而不是工程上的偏好。

Git 非常擅长保存代码历史。只有当团队有意识地把这些意图写下来,并让它始终与实现保持关联时,它才会保存这些意图。

这种缺口一直都是软件工程的一部分。AI 编码工具让它变得更显眼了。一个系统可以读完仓库里的每个文件,跟踪每个符号,并生成一个在技术上看起来很有说服力的补丁,但最终解决的仍然可能是错误的问题。

问题不在于代码具有误导性,而在于代码回答的是一个更窄的问题。

工程真相的五个层次

大多数有意义的软件变更,都依赖于五个不同层次的证据。

  1. 意图 — 用户、客户或业务真正想要的结果是什么?这可能存在于规格说明、工单、支持对话或会议记录中。
  2. 约束 — 哪些东西绝不能被破坏?兼容性承诺、安全边界、法规、合同、预算和截止日期,往往都不在仓库里。
  3. 实现 — 系统今天是如何工作的?代码、测试、schema、依赖项和部署配置构成了这一层。
  4. 运行时证据 — 真实系统里正在发生什么?日志、追踪、指标、生产数据和事故报告,可能会推翻那些在代码里看起来合理的假设。
  5. 决策历史 — 为什么会选择当前这种做法?答案藏在 pull request、设计讨论、被否决的备选方案和过往事故中。

仓库在第三层最有优势。它也包含其他几层的一部分,但很少足以完整呈现它们。

这很重要,因为软件故障往往出现在这些层与层之间的边界上。实现符合一份已经过时的规格说明。修复满足了工单要求,却违反了运维约束。测试之所以通过,是因为它们编码的是昨天的假设。代码在内部自洽,但生产数据却遵循着一种从未有人记录下来的模式。

一个在局部上正确的补丁,仍然可能是错误的改动。

代码仓库本身无法回答什么

设想这样一种情况:用户编辑了一份文档,随后搜索更新后的句子,结果里看到的却仍是旧版本。“让搜索立即更新”听起来像是一个明确的请求。代码仓库会暴露出几个可能的切入点,但它本身并不能定义什么才是正确的修复方案。

问题 可能的来源
哪个版本的文档才是权威版本? 源文档和修订历史
旧文本还残留在哪里? 同步日志、提取输出、搜索索引或缓存
对这个产品来说,“立即”具体意味着什么? 产品承诺或服务目标
文档的权限是否随着内容一起发生了变化? 源权限和审计历史
过期结果是否只局限于某个用户、来源或区域? 请求追踪和生产指标

搜索代码也许能解释结果是如何返回的,但它无法告诉你,真正的缺陷究竟是同步延迟、提取结果过期、缓存失效、权限传播问题,还是产品从未定义过的某种预期。

在成熟系统中,这种区分会变得更加重要。一个看起来已经废弃的字段,可能仍在支持某个遗留客户端。一个看似重复的服务,可能是在隔离具有不同权限要求的数据。一个表面上显得多余的检查,可能是当前团队从未亲历过的某次生产事故在代码层面留下的唯一痕迹。

删除复杂性很有价值。删除那些伪装成复杂性的历史,代价却很高。

即使有更多上下文,也仍然可能得出错误答案

显而易见的解决办法,是给 AI 更多材料:整个代码仓库、每一张工单、每一份文档、每一条消息,以及每一份日志。

这带来的是访问,不是理解。

信息源可能已经过时、彼此矛盾、带有推测性,或者是写给不同受众的。头脑风暴的内容不应凌驾于已批准的规范之上。六个月前的需求不应悄悄覆盖昨天的产品决策。生产日志应当与生成它的发布版本、环境和代码路径关联起来。客户请求在未核实适用范围之前,不应被当作普遍需求。

因此,一个严肃的上下文系统需要的不只是检索。它还需要一种方式来推理以下问题:

  • 权威性: 哪个来源有资格定义需求?
  • 时效性: 哪些信息是当前有效的,哪些已经被取代?
  • 来源: 每一项主张、约束或结论来自哪里?
  • 关系: 哪些问题、发布、客户、数据集和代码路径是彼此关联的?
  • 权限: 哪些来源可以用于这项任务,并展示给这个人?

上下文不是一堆 token。它是一个带有时间、权威性和边界的图谱。

更多上下文带来的应该是更多证据,而不是在没有证据时带来更强的信心。

真正的工作单位是变更

编辑器和编码工具围绕文件来组织,因为文件是我们修改的对象。工程团队则围绕变更来组织。

一次变更始于一个原因。它会变成需求,触及代码和数据,经过评审,进入生产,并产生新的证据。如果这些阶段彼此脱节,那么未来的每一项任务都要再来一轮考古。

一个处理真实软件的 AI 系统,应该遵循这个生命周期。

在实现之前,它应当识别请求、相关约束,以及任何相互冲突的信息源。它应当知道自己是在修复缺陷、改变预期行为,还是引入新的契约。

在实现过程中,它应当把每一个有意义的选择都与证据关联起来。为什么是这个模块?为什么采用这种迁移策略?为什么要保留这个分支?这些解释应当能够在生成代码的那次对话结束之后继续保留下来。

实施之后,它应将验证结果、评审决定以及新发现的约束附加到这次变更中。否则,下一个人——或者下一次 AI 会话——就不得不重新发现这些信息。

这就是“能编辑代码仓库的工具”和“能参与工程工作的系统”之间的区别。

AI 应该减少重建成本,而不是取代判断

更好的上下文有时被描绘成通往自主软件开发的路径。它眼下的价值没那么戏剧化,却更实用:在做出变更之前,降低重建现实的成本。

AI 可以把最初的需求放到相关代码旁边。它可以呈现出解释某个异常保护措施的事故。它可以把异常指标与引发变化的那次发布关联起来。它还可以在实施开始之前,就指出两个权威信息源之间存在分歧。

这些能力并不会消除工程判断。它们会让判断建立在更充分的信息之上。

仍然需要由人来决定哪种权衡是可以接受的、需求是否完整,以及一次发布可以承受多大风险。系统应该让证据可见,让推理过程可检查。它不应该用一个看起来很漂亮的补丁来掩盖不确定性。

衡量标准不是“它能生成代码吗?”

衡量标准是“它能解释为什么现在这是正确的变更吗?”

从感知仓库到感知工作

编码助手最初之所以变得有用,是因为它们能理解开发者眼前的那个文件。对仓库的感知是下一个重要阶段:找到相关代码、跟踪符号,并在整个项目中应用变更。

下一步,是感知围绕仓库展开的工作。

这意味着要把代码与提出它的规格说明、澄清它的对话、质疑它的生产证据,以及事后应该被记住的决策连接起来。这也意味着要排除无关、过时或超出用户权限的上下文。

在构建 Dvina 的过程中,这是我们反复回到的想法之一。工作并不是发生在单个文件、单个应用或单次对话之中。意义存在于它们之间的连接里,也存在于这些连接如何随时间变化。

代码仓库仍然至关重要。它是系统行为可执行的事实来源。只是对于产品意图、运行现实或组织记忆而言,它并不是完整的事实来源。

完整的全貌会改变最终构建出的东西

如果只有代码,一个很自然的问题是:

什么样的变更适合这个系统?

有了更广泛的上下文之后,问题就变成了:

什么样的变更适合这个系统、这个需求、这段历史,以及此刻?

第二个问题能在约束演变成回归之前就把它们识别出来。它让评审者理解实现背后的理由。它帮助新加入的团队成员明白,为什么系统会呈现出现在这个样子。它也为 AI 赋予了一个立足现实的角色:不是编辑器里的神谕,而是一个能够汇集整个工作过程中各类证据的参与者。

你的代码仓库从来都不是全部真相。

真正的机会,在于构建能够读懂其余部分的系统——并展示它们的依据。

加入 Dvina

免费注册,把你所有的工具整合进一个简单统一的工作空间。

探索更多


我们仅收集保障服务顺畅运行所必需的分析数据。