知识账本
项目里有一类信息,从代码本身推不出来:一个领域词到底指什么,当初为什么选了这套方案而不是另一套,哪里欠着一笔明知故犯的债,上次踩过的坑是什么。这些信息不写下来,就只活在某个人或某次会话的记忆里,换人、换会话之后重新试错。知识账本把这类信息落到文件里,让它跨会话、跨人留存。
本手册把这类信息分成四本账,各有固定的存放位置,多数由配套的 /td-* 命令按固定格式维护。本章讲每本账记什么、什么时候往里写、怎么防止它过期或被没用的内容填满;账本里的规则怎么升级成机器能自动检查的约束,留到下一章。
17.1 四本账各记什么
四本账各管一类从代码推不出来的信息,在本手册里各有落地位置:
| 账本 | 记什么 | 本手册的位置与维护方式 |
|---|---|---|
| 术语表 | 领域词的定义,消解容易混淆的概念 | CONTEXT.md,由 /td-glossary 播种基线、建模流程坐实 |
| 架构决策记录 | 一个难以回退的决策,以及为什么这么选 | docs/adr/ 下一个决策一个文件,目前靠建模流程顺手产出或手写 |
| 技术债账本 | 明知故犯或历史遗留的欠账,以及还债的条件 | docs/tech-debt.md,由 /td-tech-debt 登记 |
| 踩坑记录 | 遇到过的坑和触发条件,供下次动手前对照 | docs/lessons/ 存正文,一份常驻索引列出每条的触发条件 |
四本账的侧重不同,但服务同一个判断:这条信息能不能从代码读出来。能读出来的不进账本,读不出来的才值得记。架构决策尤其如此,代码只显示最终选了什么,显示不出为什么没选别的;这个为什么正是决策记录要留住的东西。
每本账的字段是固定的,因为写它的往往不是同一个人、也不是同一次会话。字段一致,后来者才能一眼看懂一条记录,机器也才能机械地检查它是否还成立。
这四本里,术语表和技术债在本手册中有专门的命令和固定格式;踩坑记录有存放位置和常驻索引,但何时补记还靠人;架构决策记录目前只约定了存放位置,没有专门的命令,下面分别说明。
17.2 什么能记,什么不能
账本最容易坏在被没用的内容填满。两条准入规则挡住大部分噪音。
第一条,只记从代码推不出来的信息。代码结构、函数签名、依赖关系这些能直接读出来的,不进账本;一旦写进去,代码一改账本就过期,反而制造矛盾。账本只留代码沉默的那部分:意图、理由、取舍、教训。术语表把这条收得最紧。一个词要进术语表,得同时满足三点:它是领域概念而非通用技术词,它真会被理解错,而且它在多个模块反复出现。
第二条,坑要有证据,先记为问题,再决定是否升级。第一次遇到一个普通问题,先如实记下现象和触发条件;再次出现说明它可能是反复踩的模式,可以评估是否需要常驻规则或机器检查。造成严重后果的问题不必等待复发。扫描或推测得出的疑似问题同理,先作为待核实的问题记下,核实成立再处理,不直接写成结论。术语表的做法是给没坐实的词标一个”待确认”,等真正理解到位再去掉这个标记。这样账本里的每一条都来自真实发生过的事,而不是一次猜测。
技术债还有一条自己的纪律:登记债务的人不负责还债。登记只为让这笔债不被忘记,还债发生在后续的功能开发、重构或清理里。技术债分两种来源。明知故犯的,在做出取舍的当下就记,它的风险是被遗忘而变成永久欠账;历史遗留的,要靠拿代码对照已定的标准去发现,它的风险是被漏掉。尤其是明知故犯的债,必须写下还债的触发条件,否则它迟早变成永久欠账。
17.3 什么事件回写哪本账
账本不是一次性写完的文档,而是在特定事件发生时被追加。把何时记绑定到具体事件上,比依赖记忆更可靠:
| 事件 | 回写哪本账 | 本手册的做法 |
|---|---|---|
| 确认了一个领域词的准确含义,或发现两个词被混用 | 术语表 | 在建模过程中坐实 CONTEXT.md 里的词 |
| 定下一个难以回退的架构选择,或推翻了之前的架构决策 | 架构决策记录 | 在 docs/adr/ 新增一条 |
| 为赶进度或控成本,明知更好的做法却没采用 | 技术债账本 | /td-tech-debt 的登记模式当场记一条 |
| 评审或排查中发现了欠账 | 技术债账本 | /td-tech-debt 的分析模式拿代码对照标准找出来 |
| 卡在一个坑上并找到绕法,尤其是同一个坑反复出现 | 踩坑记录 | 写进 docs/lessons/ 并在常驻索引里登记触发条件 |
推翻旧的架构决策时,不改写原来那条记录,而是新增一条说明它被哪条取代。旧记录留着,后来者才看得到决策是怎么演变的,以及当初的判断为什么在当时成立。
架构决策记录在本手册里目前走的是顺手沉淀这条路:在对需求和方案做对抗式追问、同时建立领域模型的过程中,一个难以逆转、不记就会让后来者费解、且确有取舍的决策,顺带落成 docs/adr/ 下的一条记录。它还没有像术语表那样配一个冷启动命令和常驻入口,所以产出依赖那条建模流程,也可以手写;把它纳入固定约定和加载,是后续要补的。
17.4 保鲜与它的局限
账本会过期。领域词会改含义,架构决策会被新决策取代,技术债会被还掉。过期的条目比没有更糟,因为它看起来仍然权威。两个办法控制这件事。
一是每条记录带上最后核对的日期,必要时附上核对时对应的代码版本。定期扫一遍,重点看那个版本之后被删除或重命名的部分,判断记录是否还成立。账本因此可以机械地查是否过期,而不是靠人凭印象。
二是还掉的债、被取代到不再有参考价值的记录,直接删除,不保留一个”已解决”的状态。账本只装还欠着的、还成立的;历史留在版本控制里,需要时回去翻。账本越短,越有人愿意读、愿意维护。
还有一类账记不住,这是本手册在实践中暴露的真问题,要如实承认。踩坑记录依赖人在卡壳或评审之后主动补一笔,而这恰恰是最容易忘的时刻:问题刚解决,注意力已经转向下一件事。本手册现有的踩坑体系就是纯人工触发的,实践里出现过很长时间一条都没记下来的情况。换个思路也未必管用:把它做成一个靠语义匹配触发的技能,仍然要人先开口说点什么才会激活,而踩坑该记的那个时刻往往没有这样一句话。真正能做到事件一到就自动发起的,是绑定在会话结束、评审完成这类事件上的钩子,由它在相应时机问一句”这次有没有坑要记”,把想起来这件事从人的记忆转移到机制。更进一步,真正反复出现、值得长期遵守的,应当按下一章的办法升级成机器自动检查的约束,从账本里的一行文字变成一道挡得住的闸门。
本章小结
项目要维护术语、架构决策、技术债、踩坑四本账,记的都是从代码推不出来的信息,在本手册里各有存放位置和维护方式。准入靠两条规则把关:只记代码沉默的部分,坑先记为问题,再根据复发、严重程度和可检查性决定是否升级。写入绑定到具体事件而非记忆,推翻旧决策用新增记录而不改旧的。账本靠核对日期和定期清理保鲜,还清或过期就删。靠人自觉维护的记录并不可靠,踩坑体系的零触发就是实例;该记的动作要尽量绑到事件和钩子上,适合确定判断的规则再升级成机器检查。
参考与出处
- 架构决策记录的定义与形态:一个决策一份短文档,记上下文、决策与后果,重点在为什么而非实现,被取代时新增记录而不改旧的:Martin Fowler: Architecture Decision Record
- 只记架构上显著或难以回退的决策,并随新决策演进:AWS: Architectural decision record process