档案 · 开发辅助文档体系全景
项目 · 某工业级多租户业务系统(已匿名)
快照 · 2026-10-11
密级 · 已脱敏:机制保留,标识符清除

让 AI 写代码, 难的从来不是 让它写。

是让它别写坏、别写漏、别把上个月刚对齐的账再写歪一次。

这份页面做了三件事:
提取一个真实项目里所有「给 AI 看的文档」的标题,
按它们各自拦住的事故分成十一层,再把撑起这些细节的两份底稿逐章解剖。
69 份文档 · 1067 条标题 · 560 份验证留痕。
01

同一个需求,两条路

第一条路是大多数人实际在走的流程,第二条路是这套东西长出来的原因。两者的差别不在提示词写得多巧,在于第 0 步有没有那十一层。

常见做法 · 一次会话

  1. 打开对话框,用自然语言描述需求。
  2. AI 读一遍仓库,输出看起来合理的代码。
  3. 本地能跑,演示通过,收工。
  4. 两周后:同一笔账在两个入口各算了一遍,对不上。
  5. 三个月后:接手的人不知道哪个开关是装饰,改了等于没改。
  6. 每一处都「看起来是对的」,没有任何一处有能力把自己变红。

这套做法 · 开工之前先存在的东西

  1. 会话自动加载一份路由索引:需求进来四步定位到该改哪。
  2. 两千行手册按锚点切片,只加载当前任务需要的那一段。
  3. 需求原文逐字抄进边界地图,禁改写;漏掉的和多做的,一律由脚本对账。
  4. 提交前两道脚本门禁,退出码不是 0 就不许提交。
  5. 跨会话交接有固定字段:陷阱、依赖、怎么算通过。
  6. 做完八项收尾检查,审计存档写明「还剩什么未知」。
  7. 十一层不是为了限制 AI,是为了敢让它碰生产系统。

所谓「AI 写代码写不好」,多数时候不是模型不行,是没人告诉它这个系统的边界在哪、红线在哪、怎么算做完。

02

规模,以及每个数字的口径

03

十一道防线

全量扫描 1067 条标题,这里不是把它们平铺出来:标题清单在 04 区逐条列全,这一区按「拦什么事故」归成十一道防线,每道讲完同一条因果链——什么时候触发、它能干什么、它解决什么问题、怎么实现、最后解决了什么。卡片顶上那排出处是指针,点一下跳到 04 区对应章节并展开;反过来 04 区每一章也写清它归入哪道防线。措辞全部脱敏:所有具体名称、表名、接口、地址一律换成功能性描述,没有替任何文件编造过一条标题。

每道防线五拍 · 标题清单在 04 区
诚实位。术语表与架构决策记录属于开发流程资产,本项目目前按需落在单份规格与交接里(含逐条编号的备选方案取舍),尚未沉淀为项目级常驻文件。缺口保留在页面上,不折算成已完成。
附

三张图:清单背后在跑什么

03 区是因果链,看着抽象。这一区把其中三条机制画出来:一张单据要在多少环节之间跳转、每一跳谁在监听;隔离为什么是两道、而且顺序不能反;动手之前怎么把「不知道」变成能对账的格子。图里不新增任何事实——每一句都能在 04 区的标题清单里找到对应条目,节点与数字取同一次真实交付的快照。

图一

一条单据要走七跳,
每一跳都有人在听

事件不是一句通知,是一份有名字的契约:谁发布、谁订阅、按什么顺序。左边那条链是一张单据的真实生命周期,每个节点下面写着接住它的那一方。

空心点

这一跳是假设,不是现状。但它对应的失效模式是真实写在清单里的:发了一条没人监听的事件,不会崩、不会报错、不会重试,只在日志留一行警告。所以「谁订阅谁」这张映射表不是文档装饰,它让漏挂订阅在评审时就暴露,而不是在上线后。

另一种死法

订阅回调里一次没被接住的异常,会让那条订阅任务永久死亡。之后每一次投递都发现「没有接收者」,于是静默跳过:进程健康、其余接口正常,只有那一个每次都等到超时才返回。这一条被单独提成一行红线,配了排查顺序——先翻日志找异常回溯,别先怀疑连接池和网络。

图二

隔离是两道防线,
而且顺序不可颠倒

这一条是被一次审计逼出来的。三个开关你随便关,但请按这个顺序读:只关第二道,数据不会串、数据库也不吭声;把第一道一起关掉,才会看见别人的行。两道单独失效都看不见,合起来才致命——而项目当时恰好同时具备这两条。

甲租户取数
自己的行自己的行别人的行整库可见
乙租户取数
自己的行自己的行别人的行整库可见

状态—

为什么这个顺序

第一道是唯一依据,第二道只是兜底,而且明文写着「不得作为省略第一道的理由」。规则里还有一条更硬的:禁止在注释里写「靠隔离层兜底」。这句话在本项目不是风格问题——它被当成了授权,后面长出一百三十余处不显式带条件的查询,而当时估计的数字是七处。

图三

动手之前:
升维对账,降维投影

同一批节点,用两把尺子各量一遍。第一把把维度拉高:终点是需求原文逐字抄,禁改写;从终点倒推「要满足它,必须存在什么」,从起点正推「手上已经有什么能直接用」,中间交给脚本做双向差集——需求没有节点承接是缺口,节点没有需求指向是镀金,倒推卡住的那一格逼出一条未知。第二把把维度压下来:双向差集是「需求 ↔ 节点」的二部图,它结构性地看不见「节点 ↔ 节点」的同构重复;于是把同一批节点换一个轴再摆一次,按(层 × 模块角色)投影,够两个以上落进同一格、且入口签名相同,才提名一件通用件。节点一个不删、顺序不变——不是合并,是投影。下面是一次真实交付的快照,编号保留原样,名称已全部换成功能性描述。

左边是「手上已经有什么能直接用」,右边是逐字钉住的需求原文。丝从两头同时往外探,两头都接上的那一格才凝固成节点;倒推探到底也没接上的,不许像黏菌那样静默收回——停在原地、发虚、钉上编号,就是下面那五条未知。接上多少条不算成绩,剩几条没人认领才算。右下角那个空着的终点是 R4:一条丝都没指向它,因为它带着一句非空的豁免理由。

R1 会不会串(租户之间 / 租户内多人) R2 为什么不带租户条件 R3 拉高维度看怎么解 R4 已豁免 · 撤销的裁定豁免理由(判据要求非空,这里给全):这一条原本要求「按人隔离」,但它指向的对象是一份写操作建议记录;同一轮里随后明确「这类裁定任何人都可以下」,按人隔离会推翻已经定下的裁定语义,所以撤销。原先承接它的节点改指 R5——因此地图上没有任何格子指向 R4,缺口依然是 0,但这一格是「有意不做」,不是「漏了」。 R5 对话内容不许全员可见
格子 · 校验 × 全库探针4 投中 · 签名相同件「门禁骨架」:读全库结构 → 逐条判据 → 打印计数与退出码。一份基座,四份薄实现。已实现 · 双判据齐
格子 · 规范 × 条款补写3 投中 · 签名相同件「条款—索引成对」:一处判据 + 一行索引,两处同轮,改完跑对账脚本点名只改一边的人。已实现 · 双判据齐
格子 · 存储 × 连接与授权2 投中 · 签名相同件「按特权分池取连接」:业务连接与高权连接两池,池本身就是防线,不靠每次记得设开关。已实现 · 双判据齐
格子 · 存储 × 迁移补齐2 投中 · 接缝已真件「三件套补齐」:安全开关 + 策略 + 强制约束一次写全。够两个就算真接缝,但重复未达三份,成本还没回来——处置写「记名不提取」并给理由,件表仍算它通过。候选 · 记名不提取
格子 · 校验 × 运行时对照2 投中 · 签名不同不提名。一个连真库读回,一个只是根因陈述——它们共享的是「都碰运行时」这个属性,不是一份接口契约。按属性抽出来的件没有接口可抽。不提名 · 伪通用件
另有 5 个单点格各 1 投中落盘归档 / 取证留档 / 对照用例 / 判定守卫 ×2。单点不成件;而且两个「判定守卫」一个在领域层一个在前端层,格子键是(层 × 角色),不同层不并格——并了就抽出一个没有实现的件。不提名
终点 5 条节点 18 个未知 5 条 缺口 0镀金 0豁免 1阻塞 0 裁定 通过 · 退出码 0
节点 · 已探明并写明承接哪条终点 未知 · 已登记,探明动作写在旁边 不阻塞 · 主动承担的风险,带理由
门槛是什么

不是「全都知道」。是每一个不知道都有编号、有探明动作、有非空理由;缺口和镀金必须为 0——有节点不承接任何需求,就是多做的;有需求没有节点承接,就是漏做的。两条主动承担的风险(云端取证需授权、事件全量枚举未做)留在页面上,标着不阻塞,不折算成已完成。抹掉任意一个节点的「承接」,对账脚本立刻把它报成镀金:这条红线是活的。

为什么还要第二把尺子

因为轴一全绿。缺口 0、镀金 0、退出码 0,十八个节点各自都有需求承接——地图是干净的,代码库照样是重复的。二部图的差集只回答「这条需求有没有人做」,不回答「这几个人是不是各写了一遍同一件事」;而且每一步都做了正确决定的局部最简,横向一比仍是五份。所以换一根轴重摆一次:不删节点、不改顺序,只改分组键,重复自己显形。判据同样活的——把任意一格的投中数改成 1,脚本立刻报空转件;把某一件的检查栏清空,它从「已实现」掉回「候选」。

04

解剖台 · 两份底稿

上面按「拦什么事故」横切,这一区按「它长在哪份文件里」纵切。真正承载「能好好写代码」这些细节的,就是两个文件名的东西:一份每次会话开头自动加载的路由索引,一份按需切片查询的开发手册。它们都不是私有魔法——文件名本身就是公开约定,任何项目明天就能建。点开每一章看它的真实骨架:标题逐条列全、照原样抄,只把可识别的名称换成功能性描述;带「正文」灰标的那几行不是原标题,是我对该节装了什么做的一句说明。每一章下面那行灰色小字是它归入的防线编号,来历和因果链写在 03 区,这一区不再重复一遍。

可抄走 · 一
常驻区只放三样东西

定位方式、红线、收尾清单。其余全部按需加载。三百行换两千行不进上下文——省的是每一轮对话的预算,不是省一次点击。

可抄走 · 二
章节要有锚点,路由表要写行数

手册的目录不是给人翻的,是给机器指路的坐标。但行号一定会漂,所以规则写成「以重新查锚点的结果为准,不信表里写死的行号」。

可抄走 · 三
每一行都要回答「它拦什么」

速查表最右一列是已知陷阱,写的是具体事故形态,不是感想。没有这一列,一张表就只是一份清单, agent 抄完仍然会踩。

05

抽掉所有细节,剩下三条

一

模型只做路由,
事实交给脚本与落盘。

能让 AI 凭记忆答的东西,就不要让它凭记忆答。计数、覆盖、对账一律由确定性脚本产出,写进文件;上下文被压缩掉,文件还在。
二

一个事实
只允许有一个真源。

同一个数字,只许一个地方算出来。凡是它的副本:写入时必须重算、历史数据要补正、默认值方向要和代码一致——三条一起对齐,缺一条就是静默错。两处各算一遍的账,最后一定有一处是错的,而且错得很安静。
三

绿灯必须有
能力变成红灯。

任何「已通过 / 未发现 / 已完成」都要能指出:如果它是错的,哪个判据会变红。判据空转的绿灯不是证据,是装饰——本项目曾被一枚装饰品骗过一整章的隔离策略。

写这十一层文档花掉的时间和 token,远小于它替你省下的那笔账:

约定一旦不落盘,模型就在后续每一次会话里逐渐失去理智——凭记忆瞎写、把库改炸、再花半天收拾残局。

事故只发生一次,
这种消耗是每天都在付。