<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>开发流程 on KnightLi的博客</title>
        <link>https://www.knightli.com/tags/%E5%BC%80%E5%8F%91%E6%B5%81%E7%A8%8B/</link>
        <description>Recent content in 开发流程 on KnightLi的博客</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-cn</language>
        <lastBuildDate>Tue, 05 May 2026 14:35:38 +0800</lastBuildDate><atom:link href="https://www.knightli.com/tags/%E5%BC%80%E5%8F%91%E6%B5%81%E7%A8%8B/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>用测试和行为描述约束 AI 写代码，少堆史山</title>
        <link>https://www.knightli.com/2026/05/05/ai-coding-tdd-bdd/</link>
        <pubDate>Tue, 05 May 2026 14:35:38 +0800</pubDate>
        
        <guid>https://www.knightli.com/2026/05/05/ai-coding-tdd-bdd/</guid>
        <description>&lt;p&gt;用 AI 写代码时，最容易出现的体验是：前期很快，后期很乱。功能刚开始能迅速搭起来，但项目一大、修改次数一多，就会出现一个 bug 改完又冒出三个 bug 的情况。&lt;/p&gt;
&lt;p&gt;这不完全是 AI 的问题。很多人写代码也会这样，只是 AI 写得更快，问题暴露得也更快。要减少这种失控，关键不是让 AI “更努力”，而是给它更清晰的边界：先说明什么结果算对，再让它写实现。&lt;/p&gt;
&lt;p&gt;TDD 和 BDD 就适合放到 AI 编程流程里。TDD 负责把“对不对”变成自动测试，BDD 负责把“是不是我要的功能”变成人能看懂的行为描述。两者结合，可以让 AI 少猜、少自由发挥，也更容易被检查。&lt;/p&gt;
&lt;h2 id=&#34;tdd-解决什么问题&#34;&gt;TDD 解决什么问题
&lt;/h2&gt;&lt;p&gt;TDD 是 Test Driven Development，也就是测试驱动开发。它的基本顺序是：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先写测试。&lt;/li&gt;
&lt;li&gt;运行测试，确认它现在失败。&lt;/li&gt;
&lt;li&gt;再写功能代码。&lt;/li&gt;
&lt;li&gt;持续修改实现，直到测试通过。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这和很多人习惯的做法相反。平时写一个排序函数，直觉上会先写函数，再随便输入几个数字看看结果对不对。TDD 则要求先把预期写成测试，例如输入 &lt;code&gt;[3, 1, 2]&lt;/code&gt; 时应该得到 &lt;code&gt;[1, 2, 3]&lt;/code&gt;，输入空数组时应该返回空数组，输入包含重复数字时结果也应该正确。&lt;/p&gt;
&lt;p&gt;这样做的意义是：开发开始前，正确结果已经被定义清楚了。后面无论谁改代码，只要重新跑测试，就能知道有没有破坏之前约定好的行为。&lt;/p&gt;
&lt;h2 id=&#34;为什么-tdd-以前不容易坚持&#34;&gt;为什么 TDD 以前不容易坚持
&lt;/h2&gt;&lt;p&gt;TDD 听起来很美，但在真实项目里并不容易长期执行。&lt;/p&gt;
&lt;p&gt;一是它反直觉。面对一个空文件时，很多人更想先把功能写出来，而不是先写测试。尤其需求还不清楚时，测试用例也很难落笔。&lt;/p&gt;
&lt;p&gt;二是需求变化快。今天认真写下的十几个测试，明天需求一改，可能就要大面积重写。短期看，它会让开发节奏变慢。&lt;/p&gt;
&lt;p&gt;三是测试本身也需要成本。测试代码不是凭空出现的，过去它需要程序员自己写、自己维护、自己解释价值。对只看短期交付速度的团队来说，这件事很容易被压掉。&lt;/p&gt;
&lt;p&gt;但 AI 改变了这个成本结构。把需求转成测试代码，恰好是 AI 很擅长的工作。让 AI 根据测试去补实现，也比让它对着一段模糊描述自由发挥可靠得多。&lt;/p&gt;
&lt;h2 id=&#34;ai-写代码时怎么用-tdd&#34;&gt;AI 写代码时怎么用 TDD
&lt;/h2&gt;&lt;p&gt;使用 AI 写功能时，可以把提示方式从“帮我实现这个功能”改成下面这个顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;让 AI 先根据需求列出测试用例。&lt;/li&gt;
&lt;li&gt;要求每个测试用例都有中文说明。&lt;/li&gt;
&lt;li&gt;先 review 测试用例是否符合真实需求。&lt;/li&gt;
&lt;li&gt;确认测试后，再让 AI 写功能实现。&lt;/li&gt;
&lt;li&gt;要求 AI 运行测试，并根据失败结果继续修正。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这时，人主要 review 的不是一大段实现代码，而是测试是否说清楚了需求。测试用例通常更接近“输入是什么、输出应该是什么、边界情况怎么处理”，比直接读实现逻辑轻松很多。&lt;/p&gt;
&lt;p&gt;例如可以这样要求 AI：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;先不要实现功能。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;请根据下面的需求编写测试用例，每个测试用例用中文注释说明覆盖的业务规则。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;测试确认后，再根据测试实现代码。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这个流程能减少两类常见问题：一类是 AI 写着写着偏题，另一类是后续修改时把旧功能改坏。&lt;/p&gt;
&lt;h2 id=&#34;tdd-还不够&#34;&gt;TDD 还不够
&lt;/h2&gt;&lt;p&gt;只有 TDD 仍然有两个缺口。&lt;/p&gt;
&lt;p&gt;第一个缺口是：测试都通过，不等于产品真的符合预期。测试只能证明代码满足了测试里写下的规则。如果测试本身没有表达清楚用户需求，代码仍然可能“正确地做错事”。&lt;/p&gt;
&lt;p&gt;第二个缺口是：测试代码对非技术用户仍然不友好。哪怕有中文注释，很多人还是不愿意阅读一堆单元测试。需求越偏产品体验，越难直接从测试代码里确认“这是不是我要的东西”。&lt;/p&gt;
&lt;p&gt;这时就需要 BDD。&lt;/p&gt;
&lt;h2 id=&#34;bdd-解决什么问题&#34;&gt;BDD 解决什么问题
&lt;/h2&gt;&lt;p&gt;BDD 是 Behavior Driven Development，也就是行为驱动开发。它关注的不是代码内部怎么写，而是系统在某个场景下应该表现出什么行为。&lt;/p&gt;
&lt;p&gt;BDD 常用的描述方式是 Given / When / Then：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Given&lt;/code&gt;：给定某个前置状态。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;When&lt;/code&gt;：当用户或系统执行某个动作。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Then&lt;/code&gt;：应该得到某个结果。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例如一个带吸血效果的游戏角色，可以这样描述：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Given 棋盘上有一个剩余 1 点生命、攻击力为 2、最大生命为 5 的吸血鬼
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And 相邻格子有一个剩余 10 点生命的敌方单位
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;When 吸血鬼攻击这个敌方单位
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Then 敌方单位剩余 8 点生命
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And 吸血鬼恢复到 3 点生命
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这段话不是代码，但它比“攻击敌人时恢复生命”精确得多。它说明了初始状态、动作和结果，也暴露出后续需要补充的问题：如果敌人只剩 1 点血，吸血鬼按造成伤害恢复，还是按攻击力恢复？如果吸血鬼已经满血，溢出的治疗怎么处理？&lt;/p&gt;
&lt;p&gt;这些问题越早被问出来，AI 后面越不容易乱猜。&lt;/p&gt;
&lt;h2 id=&#34;为什么-bdd-很适合-ai&#34;&gt;为什么 BDD 很适合 AI
&lt;/h2&gt;&lt;p&gt;BDD 过去推行成本也不低。它要求产品、开发、测试用同一套行为描述沟通，而现实里很多团队并没有这种协作习惯。&lt;/p&gt;
&lt;p&gt;但 AI 时代，BDD 的成本下降了。你只需要先写一句粗略需求，例如：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;吸血鬼攻击敌人后，恢复与造成伤害等量的生命值。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;然后让 AI 生成 Given / When / Then 场景。做得好的 AI 会主动补充边界情况，并追问不明确的规则。你需要做的是确认这些行为描述，而不是直接读实现代码。&lt;/p&gt;
&lt;p&gt;一旦行为描述确认清楚，再让 AI 把它转换成测试代码，最后根据测试实现功能，路径就顺了很多。&lt;/p&gt;
&lt;h2 id=&#34;一套更稳的-ai-编程流程&#34;&gt;一套更稳的 AI 编程流程
&lt;/h2&gt;&lt;p&gt;实际使用时，可以把 BDD 和 TDD 串起来：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先用自然语言写需求。&lt;/li&gt;
&lt;li&gt;让 AI 转成 BDD 行为场景。&lt;/li&gt;
&lt;li&gt;人确认 Given / When / Then 是否符合预期。&lt;/li&gt;
&lt;li&gt;让 AI 把行为场景转换成自动测试。&lt;/li&gt;
&lt;li&gt;人快速 review 测试覆盖范围。&lt;/li&gt;
&lt;li&gt;让 AI 实现功能。&lt;/li&gt;
&lt;li&gt;运行测试，失败就让 AI 根据错误继续修正。&lt;/li&gt;
&lt;li&gt;最后再做一次人工验收和代码 review。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这里的关键是顺序。不要一开始就让 AI 写完整实现，而是先让它把需求变成可确认的行为，再变成可执行的测试。这样 AI 的自由发挥空间会小很多。&lt;/p&gt;
&lt;p&gt;可以直接使用类似这样的提示词：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;
&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;6
&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;7
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class=&#34;lntd&#34;&gt;
&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;请按 BDD + TDD 的流程处理这个需求。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;第一步：先把需求整理成 Given / When / Then 行为场景，不要写代码。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;第二步：列出你发现的不明确规则，并向我确认。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;第三步：在行为场景确认后，再把它们转换成测试用例。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;第四步：测试确认后，再实现功能。
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;第五步：运行测试，根据失败结果修复，直到测试通过。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;这类提示词不复杂，但能明显改变 AI 的工作方式。它会先收敛需求，再进入实现，而不是一上来就写一堆看似完整、实际难以验证的代码。&lt;/p&gt;
&lt;h2 id=&#34;适合优先使用的场景&#34;&gt;适合优先使用的场景
&lt;/h2&gt;&lt;p&gt;BDD + TDD 不一定适合所有任务。对于一次性脚本、临时数据处理、小范围样式调整，完整流程可能太重。&lt;/p&gt;
&lt;p&gt;它更适合这些场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;业务规则多，容易理解错。&lt;/li&gt;
&lt;li&gt;边界条件多，后续还会持续修改。&lt;/li&gt;
&lt;li&gt;游戏、计费、权限、状态机、表单校验等逻辑密集功能。&lt;/li&gt;
&lt;li&gt;需要多人协作确认需求。&lt;/li&gt;
&lt;li&gt;代码将长期维护，不只是一次性生成。&lt;/li&gt;
&lt;li&gt;已经出现“AI 越改越乱”的项目。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果只是让 AI 改一个按钮文案，不必上完整流程。但如果要做一套角色技能系统、订单状态流转、权限判断、积分规则，先写行为场景和测试会更划算。&lt;/p&gt;
&lt;h2 id=&#34;使用时注意什么&#34;&gt;使用时注意什么
&lt;/h2&gt;&lt;p&gt;第一，测试不是越多越好。测试应该覆盖关键规则和高风险边界，而不是把实现细节全部锁死。否则需求稍微变化，测试就会变成维护负担。&lt;/p&gt;
&lt;p&gt;第二，BDD 场景要写具体。不要写“系统应该正常工作”“体验应该流畅”这类无法验证的描述。要写清楚给定什么状态、发生什么动作、结果应该是什么。&lt;/p&gt;
&lt;p&gt;第三，人仍然要 review。AI 可以生成测试和行为场景，但它不知道你真正想要的产品取舍。尤其是边界规则，必须由人确认。&lt;/p&gt;
&lt;p&gt;第四，测试通过后还要实际运行功能。自动测试能兜住逻辑问题，但界面体验、性能、交互细节、用户感受仍然需要人工验收。&lt;/p&gt;
&lt;h2 id=&#34;小结&#34;&gt;小结
&lt;/h2&gt;&lt;p&gt;AI 写代码快，但快不等于稳。越是复杂需求，越不能只靠一句“帮我实现”。更好的方式是先把需求拆成可确认的行为，再把行为变成可运行的测试，最后让 AI 按测试实现代码。&lt;/p&gt;
&lt;p&gt;TDD 让 AI 知道什么结果算对，BDD 让人更容易确认这是不是自己想要的功能。两者合起来，不是为了增加仪式感，而是为了减少 AI 的猜测空间，把“写得快”变成“改得稳”。&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
