——源生文档:重构人机协作的第一块基石
源生文档,是与代码同源、同步、同构的设计上下文。它不是代码写完再补的说明书,而是和代码共享同一个起源——那个AI理解需求、做出决策的推理瞬间。代码给机器执行,源生文档给人机共同体阅读。
这篇文章,正是它的概念宣言、标准规范与开箱即用的工具箱——三位一体,为AI时代的软件工程铺下第一块基石。
在你的项目中部署源生文档开发规范,只需将此文章完整地保存到你的项目目录中,让AI阅读后即可使用,AI帮你搞定一切,真正开箱即用!
本文的最新版发布在github,请在这里下载:
https://github.com/jsonlicn/sourcedoc
开篇:一个永不过时的问题
2026年,一个资深架构师在接手旧模块时,被一段代码卡住了。
逻辑看得懂——查库存、算折扣、下单。但他盯着那个奇怪的阈值愣了十分钟:if (order.total > 10000) 要走审批。为什么是10000?谁定的?还在生效吗?
答案不在代码里。它在三年前一封转发五层的邮件里,在一个已经离职的PM的脑子里。
他苦笑。二十年前刚入行时,被折磨的就是同一个问题。
代码解释得了How,解释不了Why。 Why是软件系统中最脆弱的资产——不编译、不运行、不报错,也最容易丢失。几十年来,每个时代都试图解决它:结构化编程用流程图,面向对象用UML,敏捷干脆说“可工作的软件高于详尽的文档”。换姿势,从未根治。
然后他想起上周让AI改这个模块。AI“优化”掉了一个边界检查——因为它不知道那是为了修复一个线上事故加的。又在一个函数里顺手抛了异常——因为不知道空数组来自上游“合同暂未关联商品”的业务状态。
更让他不安的是另一件事。三个月前AI生成的那个推荐模块,经过五轮小修小改后,已经没人敢碰了——每次修改都在无意中破坏旧的设计,代码在沉默中走向腐败。
AI没有制造新问题。它只是让三个从未被治愈的老问题暴露得更彻底——
意图流失:Why在人脑里消失,AI在黑暗中摸索。
文档腐烂:文档与代码物理分离,永远追不上变更。
渐进式腐败:每次修改都在无意中破坏旧设计,代码逐渐变成无人敢动的禁区。
解法不是更努力地写文档。是把设计意图锚定在代码旁边,让它和代码同源而生、同步更新。
我们称它为源生文档。
第一章:旧问题——软件工程几十年的结构性阴影
在讨论AI带来了什么新可能之前,需要诚实地面对一个事实:AI没有制造这些问题。它们在软件工程诞生之初就存在。每个时代都试图解决,每个时代的方案都只能缓解,不能根治。
1.1 文档的永恒之痛:同步腐烂
代码向前跑,文档原地踏步。
这个问题从软件工程诞生的第一天就存在。上世纪七十年代,结构化编程试图用流程图和详细设计文档在编码前固化逻辑——结果是需求一改,文档就变成撒谎的化石。九十年代,UML建模兴起,人们幻想“模型驱动架构”让文档自动生成代码——结果模型和代码朝着两个方向各自演化。到了敏捷时代,“可工作的软件高于详尽的文档”成为宣言——这句宣言的背面,是对“文档永远追不上代码”这个事实的战术性放弃。
几十年来,我们一直在换姿势,但从未真正解决问题。不是人不努力——是文档与代码物理分离这个结构,本身就注定了同步需要一个额外的、持续的意志力驱动。而意志力,是人类最不可靠的资源。
1.2 设计意图的慢性流失
代码解释得了How,解释不了Why。
任何一个有几年工作经验的程序员都遇到过这个场景:你完全看得懂一段逻辑在做什么——但你不理解它为什么要这么做。那个古怪的阈值、那个刻意跳过的边界条件、那个看起来多余的检查——它们从哪来?
答案通常不在代码里。它在三年前的一次需求评审里,在一封转发三层的邮件里,在一个已经离职的架构师的脑子里。
Why是软件系统中最脆弱的资产。它不编译、不运行、不报错,所以最容易丢失。这个问题不是某个时代特有的——它是软件工程这门学科与生俱来的老毛病。代码是沉默的,人是健忘的,组织是健忘的。
为了缓解这个问题,我们发明了封装、分层、设计模式——它们的核心价值只有一个:减轻人类大脑的认知负担。这完全合理。但代价一直存在:每一个抽象层后来的人都需要穿透,每一个私有封装都需要逆向理解。在纯人类协作时代,我们默认承受这个代价。但当AI也加入读者行列时,这些抽象正在变成理解障碍——它们隐藏的不仅是细节,还有意图。
1.3 渐进式代码腐败
代码质量很少一次性崩塌。它是在一次次修改中,设计信息逐渐丢失,逻辑逐渐走形。
一个新模块上线时,结构清晰,意图明确。三个月后,经过十几次小修小改——加个if分支,改个默认值,跳过某个检查——代码变成了没人敢动的禁区。不是某个提交搞砸的,是每次修改都无意中制造微小的设计偏差,累积起来改变了系统的行为预期。
这个问题在纯人类协作时代就存在。但AI放大了风险。AI每次修改都基于“当前代码的样子”,而非“代码的设计历史”。当它无法理解为什么某段逻辑这样写时,它会“优化掉”一个关键的边界处理,或在错误的位置添加新功能,破坏已有的模块边界。人类修改旧代码时会隐约感到“这里可能有什么原因”,然后停下来查一查。AI不会——它不知道那段代码里埋葬过一个线上事故。
文档记录是反腐化的手段之一,但如果记录和代码分离,它们会在同一个时间里腐烂。需要的是一个让设计意图与代码物理共存、同步更新的机制。
1.4 旧方案只能缓解,不能根治
这三重张力——同步腐烂、意图流失、渐进式腐败——不是某一年集体出现的。它们是软件工程这门学科的结构性阴影,伴随着每一次范式转移换装登场,但从未真正退场。
AI的出现不创造这些问题,也不自动解决它们。但它第一次提供了某种可能性——让同步变成事件驱动的自动化行为,让意图拥有存活于代码之旁的可检阅形式,让每一次修改都站在所有历史决策的肩膀上。
这就是接下来要展开的方向。
第二章:源生文档——定义与原则
2.1 源生文档是什么
源生文档,是与代码同源、同步、同构的设计上下文。
它不是代码写出之后补的说明书。那种说明书是“派生文档”——代码先行,文档后补,天然滞后,天然腐烂。源生文档是同一个AI推理过程产出的两个等价输出之一:代码给机器执行,源生文档给人机共同体阅读。
“源生”二字,取“同源而生”之意。它和代码共享同一个起源——那个AI理解需求、分析结构、做出决策的推理瞬间。它不是代码的影子,而是代码的孪生。
2.2 三条核心原则
原则一:同源原则
源生文档与代码是同一个AI推理过程的两个等价输出,而非先后产物。两者共享同一源头,彼此不是附属关系。
原则二:意图显式化原则
所有无法从代码文本中直接推导的设计意图、边界条件、历史决策,必须显式记录在源生文档中。代码解释How,源生文档解释Why。这不是礼貌,这是基础设施。
原则三:事件驱动同步原则
源生文档的更新由代码变更事件自动触发,而非依赖人的记忆或意志力。同步是机器行为,不是人的待办事项。“我忘了更新文档”从此不再是一个有效的借口——因为根本不需要“记得”。
2.3 四层结构
源生文档由四个层级构成,覆盖从宏观架构到微观实现的完整时间线:
设计概要定义了系统的骨架。意图文档和变更摘要分别记录了每一次修改的事前计划和事后结果。源生注释则将设计意图锚定在每一段具体代码之上——不仅是函数头部,也包括函数体内关键逻辑块的行内说明。四层联动,构成了一个完整的、持续生长的设计知识网络。
2.4 意图文档——内建的轻量规格层
意图文档是源生文档体系中一个关键创新。它吸收了“规格驱动开发”的理念——在写代码之前先明确要做什么——但将其内建为源生文档的一个有机层级,解决了一个核心问题:按次执行与连续生长的统一。
每次新任务开始时,AI不立即写代码,而是在 sourcedoc/increments/ 下创建一份意图文档,包含:任务目标、修改范围、技术方案、关键决策、以及所有无法自行确定的待确认项。人类审查者一次性确认所有待定问题,AI带着已确认的完整上下文开始实现。任务完成后,这份意图文档不会被丢弃——它成为源生文档连续性的一部分,与变更摘要形成“事前声明→事后实录”的闭环。
传统规格方案有一个问题:规格只用一次,用完就放在一边。下次开发者要改同一个功能时——比如“上次那个推荐算法,加一个维度”——AI不会自动找到那份旧规格。开发者需要手动翻出旧文档,重新喂给AI,或者干脆从零开始描述背景。
源生文档的意图层解决了这个问题。当开发者说“修改上次的推荐逻辑”,LLM会精确找到那次任务的意图文档。那里面记录了当时的方案、关键决策、哪些问题当时没确定。本次修改,直接站在上次的基础上继续。而当开发者说“做一个全新的功能,和之前无关”,LLM就只加载项目概要,不翻旧账。
按需加载,不多看,也不漏看。
2.5 意图文档的输出中断机制
为防止LLM在生成意图文档后“顺手”直接编码,意图文档必须包含一个明确的硬中断。意图文档的最后部分是编码就绪检查清单:
- [ ] 人类已确认所有决策和假设- [ ] 所有[待确认]项已有明确结论- [ ] 已检查不需要为受影响模块创建 design.mdLLM生成意图文档后必须在此处停止,明确说明:
“等待你确认以上检查项后,我将开始编码。” 这份检查清单本身也构成了每次变更的确认记录——当任务完成、意图文档归档时,它保留了一份“当时做出了哪些决策、确认了哪些假设”的完整画面,让后来的修改者理解这段设计历史。
2.6 反模式对照:意图隐藏的代价
在进入规范之前,先看一个具体的例子。它展示了“意图隐藏”在日常代码中的形态,以及源生文档的应对方式。
反模式:意图完全隐藏在实现中
function cancelOrder(order) {// 为什么大于10000就要走审批?不知道。if (order.total > 10000) {return financeApproval(order);}// xyz渠道有什么特殊?不知道。if (order.channel === 'xyz') {syncCallback(order, 'xyz');}// 正常的取消逻辑...}
这段代码能跑。但任何人——包括人类,也包括AI——修改它时,都在黑暗中摸索。那个10000的阈值是财务部规定的吗?还是某个已离职的TL拍脑袋定的?xyz渠道的回调是合同义务还是历史遗留?不知道的东西越多,修改越危险。
源生文档化后:
/**取消订单@why 统一取消入口。高额订单需走财务审批流才能取消。xyz渠道依第三方合约须同步取消状态。@boundary 订单总额>10000元时,不直接取消,返回审批中状态。订单状态为"已发货"时无法取消,抛出 OrderNotCancellableError。@sideEffects 调用财务审批系统;对xyz渠道发起外部回调。@changed 初始创建@changed 2025-03-11: 新增xyz渠道的特殊处理,见需求PRD-321*/functioncancelOrder(order: Order): CancelResult{// 高额订单需走财务审批,阈值10000元由财务部在2024Q3确认if(order.total > 10000) {return financeApproval(order);}// xyz渠道合同约定必须同步回调,不可静默取消if(order.channel === 'xyz') {syncCallback(order, 'xyz');}// 低于阈值的非渠道订单:直接取消,无额外副作用// 正常的取消逻辑...}
差别不在代码逻辑上——代码完全一样。差别在于,意图被抢救出来了。AI下次修改这个函数时,看到@why就知道那两条分支是设计决策而非代码冗余。新接手的人类程序员不需要翻三个月的聊天记录去理解xyz渠道的来历。那个从离职架构师脑子里消失的知识,现在留在了代码旁边。
2.7 源生文档不是什么
在定义“是什么”之后,同样重要的是澄清“不是什么”。
不是旧注释的改名。 旧注释解释“代码做了什么”,源生注释记录“为什么这样做”和“边界在哪”。旧注释是给不熟悉代码的人看的,源生注释是给需要修改代码的人和AI看的。
不是设计文档的替代。 需要叙事、图表、多方评审的设计文档,仍有其不可替代的场景。源生文档不消灭它们,而是成为它们与代码之间的桥梁——将设计决策锚定在代码旁边。
不是万能药。 决定性上下文仍在人脑。AI推不出的东西,源生文档里就没有。它的上限是人的表达决心。
第三章:源生文档解决了什么
一个概念的价值,取决于它解决的问题。源生文档为AI辅助开发中最棘手的四个问题提供了系统性的解决方案。这些问题不是理论推演——它们是每个使用AI编码工具的开发者,在日复一日的工作中反复遭遇的真实痛点。
3.1 解决“AI代码不可靠”
对AI编码最普遍的担忧是:它生成的代码能跑,但逻辑可能不对。
这个问题的本质不是AI能力不足——而是AI缺乏设计上下文和历史认知。AI看到代码文本,看不到文本承载的设计决策。它看到一个条件分支,看不到这是为了修复一个线上事故而加的。它看到一个奇怪的阈值,看不到这是和财务部确认过的业务规则。
源生文档通过三个机制为AI注入这些缺失的上下文:
@why 记录设计决策。 当AI看到
@why 此函数故意不处理空数组——空数组来自上游"合同暂未关联商品"的业务状态,它就不会“顺手”给空数组加一个异常抛出。它理解了这是一个设计决策,而非一个遗漏。@changed 记录修改历史。
@changed 2025-11-03: 修复并发导致重复扣款——增加乐观锁。AI读到这条记录,就不会建议“这个锁似乎多余,可以移除”。它知道这是血的教训。设计概要 记录模块依赖。 AI在修改推荐算法时,看到设计概要中标注了推荐模块与用户等级模块的依赖关系和边界约定,它就不会为了一时方便而破坏这条边界。
AI的不可靠,不是因为它笨——是因为它一直在黑暗中摸索。源生文档给了它一盏灯。
3.2 解决“AI代码审查困难”
AI让代码产出加速,但审查者的大脑速度并未同步提升。当AI一次性生成数百行代码时,审查者面对海量diff,无法快速抓住“这段代码想干什么”。认知过载之下,要么草草通过,要么干脆禁止AI生成复杂代码。
源生文档通过变更摘要改变了审查流程的根本结构。
传统审查是“先看代码,反推意图”——审查者从diff出发,逐行理解,在脑中重建修改者的设计思路。这是一个费力的逆向工程过程。
源生文档下的审查是“先读意图,验证代码”——审查者打开PR,首先看到一份变更摘要:这次改了哪几个模块、每个模块的逻辑变更是什么、为什么改、影响范围在哪、风险等级如何。带着这份已理解的意图去看diff,认知任务从“探索式理解”变为“目标式验证”。前者是开放性的,费力的;后者是聚焦性的,高效的。
审查的门槛降低了,但审查的深度反而增加了——因为审查者可以专注于“这符合设计意图吗”,而非“这到底在干什么”。
3.3 解决“交互低效与局部认知”
传统AI编码交互是一问一答。AI在深入实现时才逐个发现问题,开发者不断被打断。更致命的是,这些问题只在AI当前聚焦的局部代码中触发——它写推荐算法时问了用户等级的事,但它不会同时想到这个用户等级字段在三周前的订单模块里也有一个坑。
结果是:局部问题被回答了,但没有形成全局认知。推荐模块写对了,但它和订单模块的耦合点、和用户模块的边界,在编码时没有纳入同一份设计上下文。准确率被局部性锁死了。
意图文档从根本上改变了这个模式。新任务开始时,AI不立即写代码,而是先输出一份设计概要,将所有无法自行确定的点一次性结构化暴露:
[待确认] 用户等级的权重系数由运营提供还是技术端自行设定?[待确认] 仓库距离使用直线距离还是导航距离?精度要求多高?[假设] 并发导致库存不一致时,设计上容忍此偏差。是否接受?人只需一次性集中确认。确认之后,AI带着所有已确认的决策一口气完成实现。所有涉及用户等级的函数实现,都引用同一份设计概要中的决策记录,全局一致性在设计阶段就已被锁定。
这是“设计先于编码”的老原则,在AI时代的新形态。AI做设计,人确认设计。人从操作者变成了审查者和决策者。
3.4 解决“渐进式代码腐败”
代码质量很少一次性崩塌。它是在一次次修改中,设计信息逐渐丢失,逻辑逐渐走形。AI放大这个风险——它每次修改都基于“当前代码的样子”,而非“代码的设计历史”。当它无法理解为什么某段逻辑这样写时,它可能“优化掉”一个关键的边界处理,或在错误的位置添加新功能,破坏已有的模块边界。
这种行为是渐进式腐败的根源:每次修改都在无意中制造微小的设计偏差。这些偏差累积起来,最终让代码变成无人敢动的禁区。
源生文档通过@why、@boundary和@changed字段构成了反腐化机制。
- @why
告诉AI“不能改什么”——这是设计决策,不是代码冗余。 - @boundary
告诉AI“什么不能碰”——这些边界条件是经过思考的,不是偶然的。 - @changed
让AI知道“这段代码经历了什么”——每一次修改都是一堂课,后来的修改者都是学生。
更重要的是,源生文档要求AI在每次修改代码时,主动清理因本次修改而失效的代码。修改函数A导致内部函数B不再被调用?删除B,并将其@why归档至变更摘要。修改了某个逻辑使另一个分支不可达?删除那个分支,记录原因。
清理是修改动作的自然延伸,不是独立任务。谁让一段代码失效,谁就负责清理它。这不是需要人类提醒的可选动作,而是写入规范的强制性默认行为。不确定时标注[待确认],但不能沉默地保留。宁可留,不可错删。
文档记录加上主动清理,构成了一个完整的防腐屏障。每一次修改,代码库都在认知上保持自洽,而非在沉默中腐化。
第四章:规范细则
以下规范定义了一个完整的、可落地的源生文档体系。它是项目基础设施的一部分——与ESLint配置、TypeScript配置处于同等地位。
全局强制规则(最高优先级)
以下动作不是可选的。不执行即视为本规范执行失败:
修改任何模块前,若该模块尚无设计概要,必须先创建 sourcedoc/module/[模块名].design.md。新功能或重大变更开始时,必须先创建意图文档 intent.md。无意图文档,不得开始编码。每次提交或完成一轮修改时,必须输出变更摘要至 sourcedoc/change-log/[YYYY-MM-DD]-[简述].md并关联意图文档。无变更摘要,不得视为本次修改完成。
4.1 目录结构
项目根目录/├── SOURCEDOC_SPEC.md # 本规范文件(单文件启动入口)├── sourcedoc/ # 源生文档目录│ ├── project.overview.md # 项目概览与架构约定│ ├── module/ # 模块设计概要│ │ └── [模块名].design.md│ ├── increments/ # 意图文档归档│ │ └── [YYYY-MM-DD]-[任务简述]/│ │ ├── intent.md # 意图文档│ │ └── verification.md # 验证建议(可选)│ └── change-log/ # 变更摘要归档│ └── [YYYY-MM-DD]-[简述].md└── src/ # 源代码(源生注释在代码中)
4.2 项目设计概要模板
文件路径:sourcedoc/project.overview.md
# 项目概览## 基本信息- 项目名称:[项目名]- 技术栈:[如 TypeScript + React + Node.js]- 核心业务领域:[用2-3句话说明]## 架构约定- 目录结构:[一级目录的职责划分]- 命名规范:[文件命名、变量命名规则]- 状态管理:[使用的方案及约定]- API规范:[接口定义的位置和风格]## 核心模块及依赖- [模块A]:[职责] — 依赖 [模块B](../module/moduleB.design.md)、[模块C](../module/moduleC.design.md)- [模块B]:[职责] — 无外部依赖## 设计原则- [项目特有的设计原则和已知取舍]- [已知技术债和边界条件]4.3 模块设计概要模板
文件路径:sourcedoc/module/[模块名].design.md
触发条件(强制):
新建模块时 → 必须在编码前创建 首次修改已有模块但尚无 design.md 时 → 必须在修改前创建 本次修改改变了模块的职责、边界或依赖关系时 → 必须同步更新这个文件
# [模块名] 设计概要## 核心职责[此模块解决什么业务问题]## 模块边界- 输入:[接口/参数]- 输出:[返回值/事件]- 依赖:[依赖的其他模块](../module/xxx.design.md)## 设计决策- [决策]:[为什么这样选择]- [备选方案]:[为什么未采用]## 边界条件- [已知边界情况和处理方式]## 历史- [创建日期]- [重大变更日期及简述]
4.4 意图文档模板
文件路径:sourcedoc/increments/[YYYY-MM-DD]-[任务简述]/intent.md
意图文档是源生文档体系中连接“连续性”与“按次执行”的关键层。无意图文档,不得开始编码。
# 意图文档## 任务标识- **日期**:YYYY-MM-DD- **任务简述**:[一句话描述]- **发起原因**:[需求编号 / 线上问题 / 重构计划 / 优化]## 目标[本次变更要达成的具体目标,可列出多条]## 范围### 新增- [模块/文件]### 修改- [模块/文件]### 删除- [模块/文件](如有)## 方案概要[用几句话说明技术方案的核心思路]## 关键决策- [决策]:[为什么这样选]- [待确认] [需要人类确认的问题]- [假设] [AI做出的推断,需人类确认或推翻]## 关联- 相关设计概要:[sourcedoc/module/xxx.design.md](../module/xxx.design.md)- 受影响模块的源生注释:见 `src/xxx.ts` 中的相关函数- 历史相关意图:[YYYY-MM-DD-任务简述](../YYYY-MM-DD-任务简述/intent.md)## 编码就绪检查- [ ] 人类已确认所有决策和假设- [ ] 所有[待确认]项已有明确结论- [ ] 已检查不需要为受影响模块创建 design.md> 等待你确认以上检查项后,我将开始编码。
4.5 变更摘要模板
生成时机: 每次提交PR或完成一轮代码修改时。无变更摘要,不得视为本次修改完成。
文件路径:sourcedoc/change-log/[YYYY-MM-DD]-[简述].md
# 变更摘要## 变更概要[一句话描述本次变更的目的]## 涉及模块- [文件名]:[变更角色,如核心修改 / 新增依赖 / 接口适配]## 逻辑变更- **位置**:[文件/函数名]- **内容**:[做了什么修改]- **原因**:[为什么需要改]- **影响范围**:[下游模块 / 调用方]## 接口变更- [如有,逐一列出,标注是否向后兼容]## 删除项(如有)- **[函数名]**([文件路径])- 原职责:[简述]- 删除原因:[为什么不再需要]- 原@why归档:[粘贴原源生注释中的@why和@boundary]- Git恢复路径:`git checkout [commit/version] -- [文件路径]`## 风险评估- **风险等级**:低 / 中 / 高- **依赖影响分析**:[本次修改的接口/函数签名变化,经静态分析,影响以下文件和调用方]- **审查重点**:[列出]- **建议验证**:[方式]## 关联意图- 意图文档:[sourcedoc/increments/YYYY-MM-DD-xxx/intent.md](../increments/YYYY-MM-DD-xxx/intent.md)
4.6 源生注释规范
源生注释是编码动作的有机组成部分。它不限于函数头部,也包括函数体内关键逻辑块的说明。新建函数时,源生注释与代码同时生成。修改代码时,源生注释与代码同步更新。任何一方的缺失都视为本次编码未完成。
函数级源生注释
位置: 函数/方法上方,作为代码注释块。
强制字段:
/*** [一句话职责描述]** @why [为什么需要这个函数?它解决什么业务问题?]* @what [核心逻辑的简要描述,不超过三句话]* @boundary [边界条件:什么情况下返回空/抛异常/降级处理]* @sideEffects [副作用:是否修改参数、网络请求、读写外部状态]* @params [参数说明(如无可省略)]* @returns [返回值说明(如无可省略)]* @changed [变更记录。格式: YYYY-MM-DD: 原因 - 内容。初始创建写"初始创建"]*/
字段约束:
@why 强制。记录业务背景或设计决策。如果当前上下文无法推断,标注[待补充: 需要确认xxx]。禁止写“为了完成xx功能”——那是What,不是Why。@boundary 强制。列出此函数处理的边界情况。如果无特殊边界约束,写“无特殊边界条件”。@changed 强制。每次修改函数体时,追加一条新记录。不可覆盖或删除旧记录。这是这个函数的学习档案。
代码块级源生注释
位置: 函数体内部,关键逻辑块上方或行尾。
适用范围: 包含非显而易见的实现方式、算法选型、性能优化手段、特定技术限制的绕开方式、或需要被记录的业务规则。
格式: 不要求固定字段,但内容必须包含设计意图(抉择与原因),而非重复代码已经说清的事。
// 使用滚动哈希而非全量比对,避免大文本O(n²)性能退化const hash = rollingHash(text, windowSize);// 向下取整,上游会计系统不接受小数分const adjusted = Math.floor(price * 0.95);
这些注释与函数头的 @why 一样,是源生文档的有机组成部分——本次修改时同步生成,后续修改时同步更新,LLM修改该代码块前必须读取。
注释与代码同步原则(强制)
新建函数时:函数体和源生注释同时生成。没有源生注释的函数视为未完成。 修改函数时: @changed字段必须与代码变更同时追加。代码变更提交了而@changed未更新,视为本规范执行失败。修改代码块时:若代码块上方或行尾存在源生注释,必须同步检查该注释是否仍然准确。如果代码逻辑改变导致旧注释不成立,必须同步修改注释,不得留下不一致的注释。
这条原则没有例外。注释与代码在同一个提交中到达,在同一个审查中被检验。
4.7 主动清理规则(强制)
这是源生文档体系对抗渐进式代码腐败的执行机制。它不是需要人类提醒的可选动作,而是LLM在每次修改代码时的默认行为。
主动清理规则:
你在修改任何函数或模块时,必须在本次修改过程中自动执行以下步骤:
- 依赖影响分析:
在变更摘要的“风险评估”部分,声明本次修改的接口/函数签名变化,经静态分析后影响到的文件和调用方。这是启动清理的前置条件。 - 识别失效依赖:
基于依赖影响分析的结果,判断是否有其他函数、变量、类型、导入因本次修改而不再被任何代码使用。 - 判断是否可安全删除:
按废弃判定原则逐一排除后,确认可安全删除的直接删除。不确定的标注 [待确认],不做删除。 - 删除并归档:
删除代码,将其@why归档至变更摘要的“删除项”部分。
核心原则:
清理是修改动作的自然延伸,不是独立任务 谁让一段代码失效,谁就负责清理它 不确定时,只标注不删除。宁可留,不可错删
4.8 废弃判定原则
废弃判定不是一个独立的代码扫描动作。它是你在修改代码的过程中,识别并处理本次修改产生的失效代码时,必须遵守的判断原则。你不需要主动遍历整个项目寻找无用代码——你只需要为本次修改制造的失效负责。
“未被调用”不等于“无用代码”。 在判定某段代码是否可随本次修改一起删除时,必须逐一排除以下情况:
- 框架约定调用:
Next.js路由处理函数、Vue生命周期钩子、React Hook等,由框架自动调用而非显式引用。 - 模板/JSX/配置引用:
调用关系存在于模板或配置文件中,而非代码文本。 - 外部入口:
CLI命令、定时任务、消息队列消费者、package.json脚本入口。 - 动态调用:
obj[key]()、eval、new Function等无法静态追踪的调用模式。标注[待确认]。 - 公共接口:
export且可能被外部包或服务依赖。标注[待确认]。Go语言中大写字母开头的函数即属此类。
以上全部排除后,方可判定为本次修改导致的废弃代码,随本次修改一起删除并归档。
核心原则: 有疑问时,定为“待确认”而非“可删除”。宁可判断错(留),不可删错。
第五章:启动与工作流
5.1 单文件启动
源生文档的启动不需要搭建复杂的工具链,不需要团队统一配置。只需要一个文件——这份完整的文章本身。
将本文(从开篇到结语的完整内容)保存为 SOURCEDOC_SPEC.md,放入项目根目录。然后对LLM说:
“请读取 SOURCEDOC_SPEC.md。这篇文章既是源生文档的宣言,也是其规范。请先理解它,然后帮我创建本项目的项目级文档。问我需要哪些信息,我们一起来完善。”
LLM在阅读全文后,会理解源生文档的三条核心原则、四层结构、以及为什么这些规范值得被遵守。然后它会按照第四章的模板,与人类一起完成项目级文档的初始化。
这个文件不是规范的摘要。它就是规范本身——带着全部历史论证、设计意图和回答的完整文本。规范是骨架,文章是血肉。只有骨架会被遗忘,有血肉的文本才能被理解和执行。
从单文件到完整体系,一次对话即可完成初始化:
初始状态:项目根目录/├── SOURCEDOC_SPEC.md└── src/↓第一次对话后:项目根目录/├── SOURCEDOC_SPEC.md├── sourcedoc/│ ├── project.overview.md│ └── (目录骨架就位)└── src/↓第一个功能完成后:项目根目录/├── SOURCEDOC_SPEC.md├── sourcedoc/│ ├── project.overview.md│ ├── module/│ │ └── xxx.design.md│ ├── increments/│ │ └── 2026-05-11-xxx/│ │ └── intent.md│ └── change-log/└── src/└── [带源生注释的代码]
5.2 日常开发工作流
规范建立后,日常开发流程简化为几个标准步骤。开发者不再需要记忆模板——只需要触发对应的动作。
如果LLM在提交时遗漏了变更摘要的输出,开发者应暂停审查,要求LLM补充后再继续。这不是可跳过的步骤。
5.3 LLM上下文加载规则
为保证每次修改都站在所有历史决策的肩膀上,LLM在修改任何代码前,必须自动加载以下上下文:
sourcedoc/project.overview.md——项目全局上下文 sourcedoc/module/[相关模块].design.md——模块设计上下文 受影响函数的源生注释——函数级历史上下文(@why, @boundary, @changed) sourcedoc/increments/中最近相关的意图文档——近期变更意图 检查本任务是否已产出以下文档(若缺失,必须先补全再开始编码): 受影响模块的 design.md 本次任务的 intent.md
5.4 AI行为调试指南
当AI不遵守本规范时,以下是可直接使用的纠正指令。每一条都指向规范的具体条款。
症状: AI跳过意图文档直接生成代码。
工具箱指令:
"你违反了 SOURCEDOC_SPEC.md 第四章第4节的强制规则:无意图文档,不得开始编码。请撤销刚才生成的代码,并先按照模板在 increments/ 下创建意图文档。在编码就绪检查清单被确认之前,不得开始编码。"
症状: AI修改函数后忘记更新 @changed。
工具箱指令:
"你漏掉了源生注释的强制同步更新(第四章第6节)。请检查你刚才修改的所有函数,并为它们的 @changed 字段追加本次变更记录。初始创建的函数应标注为'初始创建'。"
症状: AI未输出变更摘要即视为完成。
工具箱指令:
"根据第四章第5节的强制规则,无变更摘要,不得视为本次修改完成。请基于本次diff生成变更摘要,写入 change-log/ 目录,并关联对应的意图文档。"
症状: AI未主动清理失效代码。
工具箱指令:
"根据第四章第7节的主动清理规则,你需要先输出本次修改的依赖影响分析,然后识别并处理因本次修改导致的失效代码。不确定是否可删除的,标注[待确认]。"
症状: AI对旧代码的用意做出错误推测。
工具箱指令:
"请读取该函数头部的 @why 和 @boundary 字段。如果你的修改建议与这些记录矛盾,请解释矛盾的原因。如果这些记录已过时,请明确说明,而非默默忽略。"
第六章:落地路径
一个新范式的最大敌人,是“太完整、太难起步”。以下是任何人都可以在今天开始的渐进式采纳路径。
立即(今天)
将本文保存为 SOURCEDOC_SPEC.md,放入当前项目的根目录。什么也不需要搭建,什么也不需要配置。规范本身的存在就已经是基础设施的一部分。
本周
与LLM一起完成项目级源生文档的初始化。创建 project.overview.md。选一个核心模块,补写它的设计概要。你会第一次看到项目的架构决策被显式地记录在一个可检索的地方。
第一个新功能
下一个需求来临时,走完整闭环:创建意图文档 → 审查确认 → 生成带源生注释的代码 → 提交时输出变更摘要。体验“站在所有历史决策的肩膀上”的感觉。
一个月后
回顾。问自己三个问题:
修改旧代码时,源生注释帮你回忆起多少设计意图?节省了多少追溯时间? 让AI做增量修改时,它的准确率有没有提升?“这次怎么又错了”是不是变少了? 代码审查时,先读变更摘要是否改变了你的审查体验?是不是更快就能进入实质审查?
如果这三个问题的答案让你满意,将源生文档写入团队的编码规范文档。将它纳入代码审查的checklist。让每个新人都知道,这个项目的代码旁边,有一份活的设计上下文在持续生长。
结语
2026年的那个下午,那个架构师最终没有重构那段旧代码。
不是因为妥协。是因为他终于理解了:那段代码不是“缺少设计”的结果,而是服务于一种新的设计——一种同时让人类和AI都能理解的、显式的、组合优先的设计。他后来在团队Wiki里,在他2019年写下那段“好代码定义”的下方,加了一行新的话:
“以上定义仍然适用于人类手写代码的时代。但在AI参与编码的今天,好代码需要多一个维度:它的设计意图,能否被另一个智能体在不知道上下文的情况下,准确理解并安全修改。”
这篇文章不宣称找到了软件工程的终极答案。我们离“完美的代码”还很远,离“完美的AI协作”更远。但源生文档指向了一个值得认真走的方向——在那个方向上,设计意图不再随着人的离职而消失,代码的每一次修改都站在所有历史决策的肩膀上,人机协作的基础不再是一次性的对话记录,而是一个可以持续生长的知识体系。
这或许就是AI时代最被低估的基础设施。
它不是一套工具配置,不是一组提示词技巧。它是一种新的约定——关于代码与知识应该以什么形态共存的约定。
而这份约定的第一块基石,我们已经铺下了。
夜雨聆风