AI Coding 实践中的一些感悟:脚手架、文档体系与测试
最近 WIP 的项目,需要人力 Review 的 effort 比之前让 Agent 撒丫子跑多了很多。这里记录一些 AI Coding 的想法、感悟,以及可能有用的技巧,分享一下。
1. 好的脚手架架构大于 Rules
好的脚手架架构大于 Rules。
持续迭代可复用的脚手架,严格的 layer 划分,再配合 Lint 和依赖管理,很大程度上可以避免 Agent 把代码写飞。
参加了几次 AI Coding 的分享直播后,我发现很多开发都在用 DDD + AI Coding 这一路数,和我之前一直在实践的方向不谋而合。
DDD 在 AI 工程里的优点很明显:
- AI 避免了 DDD 带来的繁琐编码工作;
- Human 可以 focus 到事件风暴、领域建模上;
- 在需要 Human Review 的场景下,DDD 的 Review 更清晰、更聚焦。
这里有个但是:DDD 上手我觉得还是挺有难度的。它没有一个统一的标准,只有适合的,没有固定的。我自己也掌握得不好,业务场景的磨炼也还不够。
但哪怕只是追求形式 DDD,比如依赖、分模块、分包、命名等,利用 AI 也能有比较好的效果,让项目更可控,架构更内聚,Agent 生成代码也不容易乱飞。
2. 文本类工作:分类 + 文件名编号
文本类工作可以通过分类 + 文件名编号,利用 Agent 的延展性做到自动参考。
比如在目录下放一个:
AI Coding 实践中的一些感悟:脚手架、文档体系与测试 (text)
00-template.md或者手写一个:
AI Coding 实践中的一些感悟:脚手架、文档体系与测试 (text)
01-XXX.md作为范本。
Agent 在这个目录下写入新文件时,通常会参考已有文件,模仿已有的风格和结构。也可以额外放一个
README.md,作为双重保险。3. CONTEXT.md:统一领域语言
CONTEXT.md 用于统一领域语言。为什么它很重要?
在不同场景、不同方向上,同一个意思可能会有很多种词汇表达。在当前领域内统一语言后,对 Agent 使用
rg/grep 是非常有帮助的。在 docs 体系中,统一语言对设计、分析等工作也非常有帮助。最起码可以保证在最核心的设计文件和代码编写中,不至于冒出一大堆 AI 自己起的新名词。
4. 规划 docs 体系:可追踪、可评估、可复盘
规划 docs 体系,对 Agent 来说可追踪,对 Human 来说可评估,也可以降低认知负担。
我现在会尽量做到:
AI Coding 实践中的一些感悟:脚手架、文档体系与测试 (text)
docs id <-> code <-> commit idClaude Code 在这方面做得挺好,comment、commit message 中都会带相关信息。
Docs 体系完全可以用作本地 memory。虽然偏大,也可以考虑按照质量提纯,但我目前做得不多。能通过
grep/rg 检索出对应文档,基本就够用了。5. 如果 AI Coding 共 10 斗,测试占 8 斗
如果 AI Coding 共 10 斗,测试我认为占 8 斗。
分享一下我现在在 Java 体系下做的测试工作。
5.1 领域层单元测试 + 变异测试
领域层单元测试配合变异测试,设置 coverage gate 到 90% 以上。
单元测试不只是为了覆盖代码,而是为了验证业务逻辑、小决策、校验规则这些真正容易出错的地方。
变异测试则是用来验证:你的单测到底能不能真的抓 bug,而不只是“覆盖到了”。
5.2 集成测试:Testcontainers 跑真实中间件
集成测试使用 Testcontainers,覆盖所有 Mapper、Repository 等,尽量跑真实中间件。
比如:
- PostgreSQL
- Kafka
- Redis
- 迁移脚本
- wiring
- repository 行为
这类测试的价值是覆盖真实边界,而不是 mock 一个虚假的世界。
5.3 API Test:我现在改用 Bruno
API Test 直接打真实环境。
我现在改用了 Bruno 写测试用例,非常丝滑舒适:
- 简洁的 DSL;
- 类似 Postman 的可视化 UI;
- 有 CLI;
- 有测试报告;
- file-based,可 Git 版本化。
我觉得写 API Test 时,Bruno 比 Playwright 更好用。
5.4 后端 E2E:只守核心 Happy Path
后端 E2E 测试,主要覆盖业务闭环的 happy path。
比如一个完整的增删查改流程,或者核心业务流程闭环。
不要太多。
E2E 测试改动很大,而且比较重。写得非常多、非常细,其实没什么意义。它更适合用来守住核心路径,而不是覆盖所有细节。
5.5 轻量级压测:K6
轻量级压测我现在用 K6。
主要用来做:
- 并发性能测试;
- DB 大数据量 seed;
- SQL 调优;
- 核心接口性能验证。
5.6 后续想探索的测试
后续想探索的测试主要是:
- 异常注入测试;
- 混沌测试。
其中,中间件异常注入测试在 Testcontainers 基础上还是比较好做的。
6. Harness 开发环境:给 Agent 一个干净、可运行、可测试的环境
Harness 开发环境的一个思路是:在脚手架里直接打造好该有的基础建设。
比如:
- 可观测性的接入;
- Docker Compose 启停和重置中间件;
- init 脚本;
- 比如 SigNoz 可访问的 token;
- Makefile 封装常用命令。
这样在业务开发过程中,只需要补充一些业务相关的东西,比如异步链路、请求链路里的
traceparent 上下游保留。然后就可以随时通过 Makefile 更简单地启停、重置中间件,提供一个干净的环境,让 AI 用来运行和测试。
7. 三方上下文:用 THIRDPARTY.md 维护
三方上下文可以通过
THIRDPARTY.md 维护。现在大多数三方文档已经在跟进 AI 时代,会提供
llms.txt 标准。这种可以直接引入。我现在的做法是直接把它们爬下来,这样 Agent 可以在本地
grep/rg,能检索到 link 里的更多信息。对于一些 SDK、Sample 代码仓库,可以在
THIRDPARTY.md 中提供 clone 命令。大多数情况下,这些代码仓库的 README 提供的信息已经足够 Agent 路由了。如果不够,也可以在
THIRDPARTY.md 里手动维护。如果三方提供了 MCP 和 Skill,那就更好了,直接安装。
8. 大道至简:可以考虑做减法
大道至简,可以考虑做下减法。
我现在自己安装的 Skill 只有一个
grill-me 的问答,用来梳理小 scope 的需求。其余的都通过:
AGENTS.md- docs 体系
- workflow 文档
来维护。
现在我基本上要么手撸提示词,要么直接把提示词放到文件里手动
@。在有足够 docs 管理的情况下,提示词其实可以很简单。
为什么这么做?
在我的实践过程中,我发现 Skill 如果不手动
@,经常出现两种情况:- 要用的时候没用;
- 不用的时候猛用。
所以直接写成 prompt,然后手动
@,倒也挺好。9. 关于用 Agent 提示词 Review 收敛
Review 是必要的放大器,但不是可靠的收敛机制。
没有明确验收标准和停止条件,多轮 Review 可能会从“改进”变成“漂移”。
但问题就在于:明确验收标准和停止条件本身就不好穷举。
Review -> 修改 -> 再 Review -> 再修改 的结果不一定就是好的,也可能越改越偏。所以在 1-2 轮 AI Review 后,基本上就可以停了。
10. 给人看的内容,用 HTML 的表现力远远高于文档
给人看的内容,用 HTML 的表现力远远高于普通文档。
在见到 kc 和 bolan 用 HTML 表达文档后,我果断放弃了继续给自己或者给别人看的东西都用 doc 的习惯,直接改成单页 HTML。
如果用 Claude Code,提示词很简单,自由发挥的效果都很好。
但 Codex 直接设计 UI 基本上就是屎,还不如看 Markdown。
简单的内容,用 image-to-image 生成个图也行。
附录一:我现在用的 AI 项目文档体系组织法
整体分两块:
- 根目录的“规矩文件”;
docs/目录。
规矩文件给人也给 AI 看;
docs/ 是项目的记忆库。一、根目录:一堆“规矩文件”
这些文件不装内容,装的是“在这个项目里该怎么干活”的规矩。
核心是:分层渐进式加载。
文件 | 说明 |
|---|---|
CLAUDE.md / AGENTS.md | 内容基本一致。最高指令 + 分层 Link。最高指令用了 Karpathy 的蒸馏 Skill |
CONTEXT.md / CONTEXT_TEMPLATE.md | 词典:一个概念只准用一个词,统一领域语言 |
DEVELOPMENT.md | 开发流程,说明怎么生成代码 |
ARCHITECTURE.md | 代码组织结构,说明代码该放哪、谁能依赖谁;ArchUnit Lint 确保不出错 |
CODE_STYLE.md | 代码风格规范 |
TESTING.md + 分层测试文档 | 测试总纲与各层测试细节,比如 UNIT_TESTING.md、INTEGRATION_TESTING.md、E2E_TESTING.md、API_TESTING.md |
COMMIT.md / PR.md / REVIEW.md / SECURITY.md | 怎么交活:提交信息、PR、核心 Review 清单、安全 Review |
DOCUMENT.md | 文档管理入口 |
THIRDPARTY.md | 外部依赖的文档、代码 Sample 等入口,比如 llms.txt 地址、GitHub 地址 |
二、docs/ 目录:项目的“记忆库”
2.1 从想法到落地
这部分靠
parent 串成可追溯链。目录 | 说明 |
|---|---|
idea/ | 还没想清楚要不要做的点子 |
prd/ | 要什么,产品需求; user-story/ 挂在它下面 |
spec/ | 系统该做什么 |
plan/ | 怎么做 |
task/ | plan 太大时才拆的具体任务;实践中我发现有点过度设计,最终一个没建 |
其中:
prd/和user-story/主要是给人看的;spec/、plan/、task/主要是给 Agent 用的。
2.2 决策与问题
目录 | 说明 |
|---|---|
decision/ | 为什么这么选,比如 ADR、业务决策等有取舍的重大决定 |
issue/ | 哪坏了、怎么复现、怎么验证;每次 fix 都关联代码、注释、issue 文档,降低后续 Review 的认知负担 |
analysis/ | 代码和业务分析,加深人的理解;也可用作某些目的的中间层分析,比如分析某个功能后,再根据分析结果做 code review、bug review |
design/ | 架构设计、业务设计;总览全局,承载重要的 Review 部分 |
2.3 运行与沉淀
目录 | 说明 |
|---|---|
operation/ | 线上怎么操作,运维手册 |
memory/ | 踩过的坑、攒下的经验;可由 Agent 原生 Memory 机制代替,也可落盘给人看 |
record/ | 报告、留痕、证据 |
integration/ | 对接三方的指南,从 reference 提纯精炼成 Map |
reference/ | 外部资料,单独放,不和自家文档混在一起 |
附录二:我在用的 Java 环境测试体系
分层
层 | 工具 | 范围 |
|---|---|---|
单元测试 | JUnit 5 / Surefire | 业务逻辑、校验、映射、小决策 |
变异测试 | PIT | 验证单测真的能抓 bug,而不只是覆盖到 |
集成测试 | Testcontainers / PostgreSQL / Kafka / Redis | 真实边界:DB、迁移、消息、wiring |
启动冒烟 | @SpringBootTest + Testcontainers | 真实扫描和装配,补 slice 测试的盲区 |
API Test | Bruno / .bru / bru CLI | HTTP 契约:状态码、响应壳、幂等、鉴权 |
E2E | Spring Boot 全量启动 + Testcontainers | 核心代码的业务闭环 happy path |
轻量级压测 | K6 | 并发性能 + 大数据量 seed + SQL 调优 |
取舍
- 门禁卡在领域层:覆盖率 + 变异测试 ≥ 90%,其他层不强压覆盖率。
- API Test 换成 Bruno:之前用 Playwright。Bruno 的优点是 DSL 简洁、可视化 UI、CLI 和报告都比较完整,file-based 可 Git 版本化,写 API 测试更顺。
- E2E 宁少勿多:重、改动大,写得多而细没有边际收益,只守闭环 happy path。
- API/E2E 测试必须能对脏库重跑:幂等键每次运行现生成,不要硬编码。