<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>BDD on KnightLi Blog</title>
        <link>https://www.knightli.com/en/tags/bdd/</link>
        <description>Recent content in BDD on KnightLi Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en</language>
        <lastBuildDate>Tue, 05 May 2026 14:35:38 +0800</lastBuildDate><atom:link href="https://www.knightli.com/en/tags/bdd/index.xml" rel="self" type="application/rss+xml" /><item>
        <title>Use Tests and Behavior Descriptions to Keep AI Coding Under Control</title>
        <link>https://www.knightli.com/en/2026/05/05/ai-coding-tdd-bdd/</link>
        <pubDate>Tue, 05 May 2026 14:35:38 +0800</pubDate>
        
        <guid>https://www.knightli.com/en/2026/05/05/ai-coding-tdd-bdd/</guid>
        <description>&lt;p&gt;When you use AI to write code, the common pattern is easy to recognize: the beginning feels fast, and the later stages get messy. A feature can be scaffolded quickly at first, but once the project grows and the number of changes increases, fixing one bug can easily create three more.&lt;/p&gt;
&lt;p&gt;This is not entirely an AI problem. Many human developers write code this way too. AI simply writes faster, so the problems surface faster. To reduce this loss of control, the key is not to make AI “try harder”, but to give it clearer boundaries: define what counts as correct first, then ask it to implement.&lt;/p&gt;
&lt;p&gt;TDD and BDD fit naturally into an AI coding workflow. TDD turns “is this correct?” into automated tests. BDD turns “is this the feature I actually want?” into behavior descriptions that humans can read. Used together, they reduce guessing, limit free interpretation, and make the result easier to review.&lt;/p&gt;
&lt;h2 id=&#34;what-tdd-solves&#34;&gt;What TDD Solves
&lt;/h2&gt;&lt;p&gt;TDD stands for Test-Driven Development. Its basic sequence is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write the test first.&lt;/li&gt;
&lt;li&gt;Run the test and confirm that it fails.&lt;/li&gt;
&lt;li&gt;Write the feature code.&lt;/li&gt;
&lt;li&gt;Keep adjusting the implementation until the test passes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This is the opposite of how many people naturally work. If you are writing a sorting function, the intuitive approach is to write the function first, then try a few inputs and see whether the results look right. TDD asks you to write the expected behavior as tests first. For example, input &lt;code&gt;[3, 1, 2]&lt;/code&gt; should return &lt;code&gt;[1, 2, 3]&lt;/code&gt;, an empty array should return an empty array, and an array with duplicate values should still be sorted correctly.&lt;/p&gt;
&lt;p&gt;The point is that the correct result is defined before development begins. Later, no matter who changes the code, rerunning the tests tells you whether previously agreed behavior has been broken.&lt;/p&gt;
&lt;h2 id=&#34;why-tdd-used-to-be-hard-to-keep-up&#34;&gt;Why TDD Used to Be Hard to Keep Up
&lt;/h2&gt;&lt;p&gt;TDD sounds great, but it is not easy to practice consistently in real projects.&lt;/p&gt;
&lt;p&gt;First, it feels counterintuitive. When facing an empty file, many people would rather write the feature first than write tests first. This is especially true when the requirement is still unclear, because test cases are hard to write when the behavior itself is fuzzy.&lt;/p&gt;
&lt;p&gt;Second, requirements change quickly. A dozen carefully written tests today may need to be rewritten tomorrow after the requirement changes. In the short term, TDD can slow the development rhythm.&lt;/p&gt;
&lt;p&gt;Third, tests have their own cost. Test code does not appear out of nowhere. In the past, developers had to write it, maintain it, and explain its value. In teams that only care about short-term delivery speed, this work is easy to squeeze out.&lt;/p&gt;
&lt;p&gt;AI changes that cost structure. Turning requirements into test code is exactly the kind of work AI is good at. Asking AI to implement against tests is also far more reliable than asking it to freely interpret a vague paragraph.&lt;/p&gt;
&lt;h2 id=&#34;how-to-use-tdd-when-ai-writes-code&#34;&gt;How to Use TDD When AI Writes Code
&lt;/h2&gt;&lt;p&gt;When using AI to build a feature, change the prompt from “implement this feature for me” into this sequence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ask AI to list test cases from the requirement first.&lt;/li&gt;
&lt;li&gt;Require each test case to include a plain-language explanation.&lt;/li&gt;
&lt;li&gt;Review whether the test cases match the real requirement.&lt;/li&gt;
&lt;li&gt;After confirming the tests, ask AI to implement the feature.&lt;/li&gt;
&lt;li&gt;Ask AI to run the tests and keep fixing based on failures.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;At this point, the main thing you review is no longer a large block of implementation code. Instead, you review whether the tests describe the requirement clearly. Test cases are usually closer to “what is the input, what should the output be, and how should edge cases behave”, which is much easier than reading implementation logic directly.&lt;/p&gt;
&lt;p&gt;For example, you can ask AI like this:&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;Do not implement the feature yet.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Write test cases based on the requirement below. Add a plain-language comment to each test case explaining the business rule it covers.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;After the tests are confirmed, implement the code according to the tests.
&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;This workflow reduces two common problems: AI drifting away from the requirement while coding, and later changes breaking old behavior.&lt;/p&gt;
&lt;h2 id=&#34;tdd-is-not-enough&#34;&gt;TDD Is Not Enough
&lt;/h2&gt;&lt;p&gt;TDD alone still leaves two gaps.&lt;/p&gt;
&lt;p&gt;The first gap is that passing tests does not mean the product actually meets expectations. Tests only prove that the code satisfies the rules written into the tests. If the tests themselves fail to express the user need clearly, the code may still “correctly do the wrong thing”.&lt;/p&gt;
&lt;p&gt;The second gap is that test code is still unfriendly to non-technical users. Even with plain-language comments, many people do not want to read through a pile of unit tests. The more product-oriented a requirement is, the harder it is to confirm from test code alone that “this is what I wanted”.&lt;/p&gt;
&lt;p&gt;That is where BDD helps.&lt;/p&gt;
&lt;h2 id=&#34;what-bdd-solves&#34;&gt;What BDD Solves
&lt;/h2&gt;&lt;p&gt;BDD stands for Behavior-Driven Development. It focuses less on how code is written internally and more on how the system should behave in a given scenario.&lt;/p&gt;
&lt;p&gt;BDD often uses the Given / When / Then format:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Given&lt;/code&gt;: a specific starting state.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;When&lt;/code&gt;: an action performed by the user or system.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Then&lt;/code&gt;: the expected result.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example, a game character with a lifesteal effect can be described like this:&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 there is a vampire on the board with 1 remaining HP, 2 attack, and 5 max HP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And an adjacent enemy unit has 10 remaining HP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;When the vampire attacks that enemy unit
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Then the enemy unit has 8 remaining HP
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;And the vampire recovers to 3 HP
&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;This is not code, but it is much more precise than “recover health when attacking an enemy”. It describes the initial state, the action, and the result. It also exposes rules that need clarification: if the enemy only has 1 HP left, should the vampire recover based on damage dealt or attack value? If the vampire is already at full health, what happens to excess healing?&lt;/p&gt;
&lt;p&gt;The earlier these questions appear, the less AI has to guess later.&lt;/p&gt;
&lt;h2 id=&#34;why-bdd-fits-ai-so-well&#34;&gt;Why BDD Fits AI So Well
&lt;/h2&gt;&lt;p&gt;BDD also used to have a high adoption cost. It asks product, engineering, and testing teams to communicate with the same behavior descriptions. In reality, many teams do not have that collaboration habit.&lt;/p&gt;
&lt;p&gt;In the AI era, the cost of BDD drops. You can start with a rough requirement such as:&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;After the vampire attacks an enemy, it recovers health equal to the damage dealt.
&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;Then ask AI to generate Given / When / Then scenarios. A good AI will add edge cases and ask about unclear rules. Your job is to confirm those behavior descriptions, not read the implementation code directly.&lt;/p&gt;
&lt;p&gt;Once the behavior descriptions are clear, ask AI to convert them into tests, and then implement the feature based on those tests. The path becomes much smoother.&lt;/p&gt;
&lt;h2 id=&#34;a-more-reliable-ai-coding-workflow&#34;&gt;A More Reliable AI Coding Workflow
&lt;/h2&gt;&lt;p&gt;In practice, you can chain BDD and TDD together:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write the requirement in natural language.&lt;/li&gt;
&lt;li&gt;Ask AI to convert it into BDD behavior scenarios.&lt;/li&gt;
&lt;li&gt;Confirm whether the Given / When / Then scenarios match your expectation.&lt;/li&gt;
&lt;li&gt;Ask AI to convert the behavior scenarios into automated tests.&lt;/li&gt;
&lt;li&gt;Quickly review test coverage.&lt;/li&gt;
&lt;li&gt;Ask AI to implement the feature.&lt;/li&gt;
&lt;li&gt;Run the tests. If they fail, ask AI to fix the code based on the errors.&lt;/li&gt;
&lt;li&gt;Finish with manual acceptance and code review.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The key is the order. Do not ask AI to write the full implementation at the beginning. First ask it to turn the requirement into reviewable behavior, then into executable tests. This leaves much less room for free interpretation.&lt;/p&gt;
&lt;p&gt;You can use a prompt like this:&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;Handle this requirement using a BDD + TDD workflow.
&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;Step 1: First organize the requirement into Given / When / Then behavior scenarios. Do not write code.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 2: List any unclear rules you find and ask me to confirm them.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 3: After the behavior scenarios are confirmed, convert them into test cases.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 4: After the tests are confirmed, implement the feature.
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;Step 5: Run the tests and fix failures until all tests pass.
&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;This kind of prompt is not complicated, but it can noticeably change how AI works. It narrows the requirement first, then moves into implementation, instead of immediately producing code that looks complete but is hard to verify.&lt;/p&gt;
&lt;h2 id=&#34;where-to-use-it-first&#34;&gt;Where to Use It First
&lt;/h2&gt;&lt;p&gt;BDD + TDD is not necessary for every task. For one-off scripts, temporary data processing, or small style tweaks, the full workflow may be too heavy.&lt;/p&gt;
&lt;p&gt;It is better suited to these cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Business rules are numerous and easy to misunderstand.&lt;/li&gt;
&lt;li&gt;There are many edge cases, and the feature will continue to change.&lt;/li&gt;
&lt;li&gt;Logic-heavy features such as games, billing, permissions, state machines, and form validation.&lt;/li&gt;
&lt;li&gt;Multiple people need to confirm the requirement together.&lt;/li&gt;
&lt;li&gt;The code will be maintained for a long time, not generated once and thrown away.&lt;/li&gt;
&lt;li&gt;The project already shows signs of AI making things messier after each change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you only need AI to change the text on a button, you do not need the full workflow. But if you are building a character skill system, order state transitions, permission checks, or points rules, writing behavior scenarios and tests first is usually worth it.&lt;/p&gt;
&lt;h2 id=&#34;what-to-watch-out-for&#34;&gt;What to Watch Out For
&lt;/h2&gt;&lt;p&gt;First, more tests are not always better. Tests should cover key rules and high-risk boundaries, not lock every implementation detail in place. Otherwise, even a small requirement change can turn the tests into a maintenance burden.&lt;/p&gt;
&lt;p&gt;Second, BDD scenarios must be specific. Do not write unverifiable descriptions like “the system should work normally” or “the experience should be smooth”. Be clear about the state, the action, and the expected result.&lt;/p&gt;
&lt;p&gt;Third, humans still need to review. AI can generate tests and behavior scenarios, but it does not know the product tradeoffs you actually want. Boundary rules in particular must be confirmed by a human.&lt;/p&gt;
&lt;p&gt;Fourth, after tests pass, you still need to run the feature for real. Automated tests can catch logic problems, but interface experience, performance, interaction details, and user feel still need manual acceptance.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary
&lt;/h2&gt;&lt;p&gt;AI writes code quickly, but speed is not the same as stability. The more complex the requirement is, the less you should rely on a single “help me implement this” prompt. A better approach is to break the requirement into reviewable behavior, turn that behavior into executable tests, and then let AI implement against those tests.&lt;/p&gt;
&lt;p&gt;TDD tells AI what counts as correct. BDD makes it easier for humans to confirm whether the feature is actually what they wanted. Together, they are not about adding ceremony. They are about reducing the space for AI to guess, turning “writes fast” into “changes safely”.&lt;/p&gt;
</description>
        </item>
        
    </channel>
</rss>
