边界的规划
这篇文章说明 TavernHeadless 用什么样的方式守住项目的边界, 以及为什么文档和测试在这个项目里被放到了和代码同等的位置。
以往项目的教训
以前我也做过不少项目。在那些项目里,常有一种冲动:这个功能看起来不错,可以加进来;那个项目有个实现挺巧妙,可以抄过来。
每次加一点,每次抄一点,项目就慢慢膨胀起来。功能之间没有清晰的边界,模块之间互相牵扯,改一个地方常常需要连带改很多地方。最后变成一座很难再动刀的屎山。
这不是能力问题。这是流程问题。
如果没有一套明确的机制来约束什么应该进、什么不应该进、什么东西应该放在哪里,任何项目都会朝这个方向滑落。LLM 辅助开发只会加速这个过程——它能写得更快,也就意味着在没有边界约束的情况下,它能更快地把项目变成屎山。
这个项目太重要了
TavernHeadless 和以往的项目不一样。
它不是一个小工具,不是一个用完就扔的脚本,也不只是一个实验。它是一套需要长期维护、需要被其他人接起来继续用的引擎。
这个定位让我不得不重新审视以前写项目的理念和流程。不是技术问题——技术再难,拆开来看都是可以解决的。真正的问题在于:怎么保证项目在持续迭代中不失控。
答案我放在了文档和测试上面。
六十五份文档
在我本地的工作区里(GitHub 上目前看不到),TavernHeadless 已经有了六十五份设计文档。去掉空白行,两万七千行。
这个数字不是为了显得多。它说明了一件事:在这个项目里,每一个重要的功能、每一次系统级的改动,在代码写出来之前,都先经过了文档。
这些文档分为三类。
Design:冻结设计方向
Design 文档负责在某个时间点、某个阶段下,把一个功能或一个系统的设计方向固定下来。
它不写实现细节。它写的是:这个东西要解决什么问题,边界在哪里,和其他系统是什么关系,有哪些明确的约束。
Design 文档是起点。后来者可以随时回到这里,理解当时为什么这样决定。也可以在下一个阶段重新审视它:当时的前提是否还成立,当时的约束是否需要调整。
Plan:指导落地实施
Plan 文档从 Design 出发,把设计拆成可执行的步骤。
它写的是:先做什么,后做什么,每一步的验收标准是什么,哪些部分可以并行,哪些部分必须串行。
Plan 文档在 TavernHeadless 里还有一个实际用途:指导 LLM 编写代码。当 LLM 有了一份明确的 Plan,它就不容易偏离方向。Plan 越清楚,LLM的产出越可控。
Review:记录审查过程与结论
Review 文档记录 LLM 对某个系统模块的审查过程和结论。
在我这里,Review 的重要性和 Design 是同一个级别的。
Design 说的是我们想做什么。Review 说的是我们做成了什么、做成了什么样、哪些地方和 Design 有偏差、哪些地方需要重新考虑。
Design 是计划。Review 是事实。两个合在一起,才能看出项目的真实状态。
测试和文档一样重要
很多人觉得 LLM 写的测试不靠谱。即使要求了覆盖率,LLM 写出来的测试也找不到真正的 bug。
我不这么看。
测试不是留给现在的,是留给未来的
LLM 写的测试也许不够完善,也许覆盖不到所有边界情况。但大方向基本是对的——它测了该测的函数,模拟了该模拟的场景,验证了该验证的路径。
这些测试的真正价值,不在写出来的那一刻。
当将来有人要改进一个系统模块时,这些测试就会起作用。如果一个改动不小心破坏了模块的基本行为,测试会立刻失败。除非这个改动的本意就是要打破旧有行为,否则这个失败就是在提醒改动者:你碰到了不该碰的地方。
这就是测试作为边界的作用。它不是用来保证代码完美的。它是用来保证代码不被无意间弄坏的。
不止于单元测试
测试不止是单元测试。
集成测试验证多个模块协作的结果。联调测试验证真实环境下的行为。
对于客户端开发来说,联调测试尤其重要。我自己也基于 TavernHeadless在开发一个客户端,在联调过程中已经为后端引擎发现了数个实实在在的问题,并且都修正了。
这些问题不是单元测试能发现的。它们只在真实的客户端请求、真实的 LLM响应、真实的会话流程中才会暴露出来。如果没有联调测试,这些问题会一直留到使用者手里才会被发现。
文档和测试共同构成边界
文档管的是设计边界。什么该做、什么不该做、做到什么程度。
测试管的是行为边界。什么能变、什么不能变、变了之后谁来报警。
两套东西放在一起,项目才不会在迭代中慢慢滑向屎山。
这不是什么高深的方法论。它很朴素:
- 做之前先写下来。
- 做完之后审查一遍。
- 把关键的行为用测试锁定。
- 每次改动都让测试跑一遍。
麻烦,但有效。
Vibe Coding 需要更强的边界意识
最后说一句关于 Vibe Coding 的事。
Vibe Coding 确实能大幅提高写代码的速度。但速度是一把双刃剑——在没有约束的情况下,速度越快,失控越快。
所以在这个项目里,Vibe Coding 从来不是"用 LLM 随便生成代码然后提交"的意思。它始终被限定在 Design → Plan → Code → Review 的流程里。
每一段代码,在写之前有 Design 和 Plan 约束方向,在写之后有 Review比对事实,在提交之后有测试锁定行为。
Vibe Coding 负责的是 Code 这一步的速度。其他三步负责的是不让速度变成失控。
这就是 TavernHeadless 的边界规划。