<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: olsemeno</title>
    <description>The latest articles on DEV Community by olsemeno (@olsemeno).</description>
    <link>https://dev.to/olsemeno</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4206407%2F2531997f-4a1a-42cc-9224-8f5e43ba384f.png</url>
      <title>DEV Community: olsemeno</title>
      <link>https://dev.to/olsemeno</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/olsemeno"/>
    <language>en</language>
    <item>
      <title>Stick to Your Guns: TDD When AI Writes the Code</title>
      <dc:creator>olsemeno</dc:creator>
      <pubDate>Sun, 25 Oct 2026 11:00:22 +0000</pubDate>
      <link>https://dev.to/olsemeno/stick-to-your-guns-tdd-when-ai-writes-the-code-dp8</link>
      <guid>https://dev.to/olsemeno/stick-to-your-guns-tdd-when-ai-writes-the-code-dp8</guid>
      <description>&lt;p&gt;You probably remember how hard it was to explain to the business why we needed to set aside a substantial percentage of time for writing tests, and why we had to build tools for technical quality control even before we had a product. It would seem that this is already in the past. But is it?&lt;/p&gt;

&lt;p&gt;Is there still a place for TDD? And is there still a place for a human among AI agents?&lt;/p&gt;

&lt;p&gt;And if so, how do we use this advantage wisely?&lt;/p&gt;

&lt;h2&gt;
  
  
  A Bit of Research
&lt;/h2&gt;

&lt;p&gt;The first claim I start from: tests before code noticeably improve generation &lt;a href="https://arxiv.org/abs/2402.13521" rel="noopener noreferrer"&gt;[1]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When tests were added to the prompt, GPT-5-nano's share of unsolved hard tasks dropped from ~76% to ~51%. For all models, pass@1 grew roughly twofold &lt;a href="https://arxiv.org/abs/2512.21028" rel="noopener noreferrer"&gt;[2]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Second: if tests are written after the code, they turn out to be tied to the current behavior &lt;a href="https://arxiv.org/abs/2412.14137" rel="noopener noreferrer"&gt;[3]&lt;/a&gt;. That is, tests written from the code rather than from the spec systematically tend to lock in the current behavior, bugs included. Besides that, a model tends to ignore a test that contradicts "popular practice" &lt;a href="https://arxiv.org/abs/2505.09027" rel="noopener noreferrer"&gt;[4]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An important point: when the model writes the tests, the tests and the code "come to an agreement" with each other and miss the requirements together &lt;a href="https://arxiv.org/abs/2608.16742" rel="noopener noreferrer"&gt;[5]&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests in the specification, reviewed and locked in by the developer before the code is written, are still an important component&lt;/strong&gt; and give a tangible gain in both productivity and correctness of the code relative to the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Write Tests Without Sacrificing Productivity?
&lt;/h2&gt;

&lt;p&gt;Writing scenarios by hand would probably be an unaffordable luxury. The pace of service development, as I feel it, is pushing my brain to its limits, and systematizing and constraining the flow of text generated by the model is becoming vitally necessary.&lt;/p&gt;

&lt;p&gt;The specification doesn't need to cover all possible scenarios. This is the place where machine and human still work together, and the text still has to be human-readable and straightforward.&lt;/p&gt;

&lt;p&gt;According to the TENET study &lt;a href="https://arxiv.org/abs/2509.24148" rel="noopener noreferrer"&gt;[6]&lt;/a&gt;, 3–5 tests covering different usage scenarios give a better result than the full test suite: an excess of tests overloads the model and reduces accuracy.&lt;/p&gt;

&lt;p&gt;But TENET used smaller tasks. In my experience, breaking the work into subtasks and covering each with &lt;strong&gt;5–10 main scenarios&lt;/strong&gt; is an excellent choice. This way it's still easy to hold the context, and it's more than enough to steer code generation in the right direction.&lt;/p&gt;

&lt;p&gt;There is another interesting option — TiCoder &lt;a href="https://arxiv.org/abs/2404.10100" rel="noopener noreferrer"&gt;[7]&lt;/a&gt;. The model clarifies your intent through tests before producing code. This works great on small but critical and high-load parts of the code. But on large standard tasks there is a noticeable loss of time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizing Tests
&lt;/h2&gt;

&lt;p&gt;For describing test scenarios I set aside the bottom part of the spec. I often go back to it when I need to refresh my knowledge of some functionality: the PRD doesn't give the depth of elaboration that has already been done, and the spec is overloaded with technical information.&lt;/p&gt;

&lt;p&gt;I describe cases in one or two sentences. It's important to include the expected result — this is the main value of a test when developing with AI. I avoid mocks at all costs and work out scenarios with external teams as much as possible, to reproduce every case. I ask for webhooks on the external API side to activate the needed case. This way the agent gets the maximum opportunity to catch a potential problem.&lt;/p&gt;

&lt;p&gt;With this approach, any mistake by one of the teams is caught as fast as possible by the tests of the other teams. Work goes according to pre-agreed APIs and specs.&lt;/p&gt;

&lt;p&gt;In the spec it looks quite short and readable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Draft can be saved with zero audiences; publish and re-activate return 400 until at least one is attached. Once attached, publish succeeds; wiping attachments then activate → 400 again&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the code, each test already needs to be expanded to simplify review. I leave the description of each test in this format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// TEST-ID: GA-U4&lt;/span&gt;
&lt;span class="cd"&gt;/// REQUIREMENT: §5.2.1 — an unknown order is a typed refusal, not a transport error&lt;/span&gt;
&lt;span class="cd"&gt;/// LEVEL: e2e (live partner)&lt;/span&gt;
&lt;span class="cd"&gt;/// MOCKS: none&lt;/span&gt;
&lt;span class="cd"&gt;/// REAL:  Platinum Staging over HTTPS&lt;/span&gt;
&lt;span class="cd"&gt;/// GIVEN: an order id no one has ever created&lt;/span&gt;
&lt;span class="cd"&gt;/// WHEN:  GET /orders asks for it&lt;/span&gt;
&lt;span class="cd"&gt;/// THEN:  the client surfaces ORDER_NOT_FOUND as a stable code&lt;/span&gt;
&lt;span class="cd"&gt;///&lt;/span&gt;
&lt;span class="cd"&gt;/// The poller leans on this: an order Platinum has never heard of must be&lt;/span&gt;
&lt;span class="cd"&gt;/// distinguishable from an edge that is down, because one is a young order&lt;/span&gt;
&lt;span class="cd"&gt;/// whose creation is still in flight and the other is an outage. Both arrive&lt;/span&gt;
&lt;span class="cd"&gt;/// as an Err, and only the code tells them apart.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Such comments are convenient to review together with the QA team. Eagle-eyed QA engineers will quickly fix the missed spots, and at the same time understand the developer's way of thinking and where bugs are possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mocks and Unit Tests
&lt;/h2&gt;

&lt;p&gt;In my practice, unit tests and tests with mocks give a small gain in code accuracy. Very often the model locks in wrong behavior with unit tests and then builds on it.&lt;/p&gt;

&lt;p&gt;IMHO, if an integration test is impossible, the right scenario is a detailed review of the mock and the input/output. This is where models have the biggest room for mistakes.&lt;/p&gt;

&lt;p&gt;If I can cover all the functionality described in the PRD with e2e tests, I do without unit tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without real requests to external APIs, models almost always make mistakes.&lt;/strong&gt; This needs to be taken into account, and the environment should be built with the possibility of such communication already at the design stage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Case: A Race Condition Between Services
&lt;/h2&gt;

&lt;p&gt;A good example. I joined an existing project that uses a third-party service: it can run simple scripts, store a user profile, and has a decent UI and API. One of my first tasks was to deal with a race condition between the services. This is a rather complex engineering task, and for a new person on the project — a real challenge. There were no integration tests on the project, so every attempt to reproduce the bug manually or with mocks failed, and the bug kept coming back to me, causing irritation first for me and then for QA (understandably).&lt;/p&gt;

&lt;p&gt;In such a situation the model is no helper at all: analyzing one system out of two is not enough.&lt;/p&gt;

&lt;p&gt;The solution was to agree with the team on spinning up a test environment. I used dev containers and a new test environment in the external service with limited keys. As soon as this tool appeared, fixing the hard task became a matter of a few hours. And the work on the infrastructure for e2e tests itself took no more than a couple of days — on a big service of a huge fintech project. &lt;strong&gt;Those two days paid off on the very first hard task.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In my experience, e2e tests are also the best entry point for a new employee: having on hand many tests related specifically to their feature, they get into the context faster. This is a great addition to reading the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Endgame
&lt;/h2&gt;

&lt;p&gt;So is there still a place for TDD? The more I work with AI, the more I'm convinced that TDD opens up in a new way in the current conditions. And there is still a place for a human too, it has just shifted: the model writes the code, and the human decides what counts as correct behavior and locks it in tests before the first line of code.&lt;/p&gt;

&lt;p&gt;What I take into every project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test scenarios in the spec, written and locked in before the code;&lt;/li&gt;
&lt;li&gt;5–10 key scenarios per subtask, not full coverage;&lt;/li&gt;
&lt;li&gt;e2e with real partner APIs instead of mocks wherever possible;&lt;/li&gt;
&lt;li&gt;a description of each test: requirement, level, what's real, what's mocked, and why the test matters;&lt;/li&gt;
&lt;li&gt;tests from the spec are untouchable, the model's tests live and change together with the code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The next step I see is QA and DEV working together on the technical spec. IMHO the model should get the maximum of input data at the moment the code is written, while in the classic approach the input data is prepared by the developer, and documents from QA come into work only after the code is written. The earlier QA scenarios appear in the spec, the fewer bugs will make it to them.&lt;/p&gt;

&lt;p&gt;After that, we can set the model loose. And all that's left for us is to roll up our sleeves and, with pleasure, turn the result into something elegant and, most importantly, understandable :)&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Mathews, N. S., Nagappan, M. &lt;a href="https://arxiv.org/abs/2402.13521" rel="noopener noreferrer"&gt;Test-Driven Development for Code Generation&lt;/a&gt;. ASE 2024.&lt;/li&gt;
&lt;li&gt;Ben Sghaier, O., Delcourt, K., Sahraoui, H. &lt;a href="https://arxiv.org/abs/2512.21028" rel="noopener noreferrer"&gt;Artificial or Just Artful? Do LLMs Bend the Rules in Programming?&lt;/a&gt; 2025 (BigCodeBench-Hard).&lt;/li&gt;
&lt;li&gt;Mathews, N. S., Nagappan, M. &lt;a href="https://arxiv.org/abs/2412.14137" rel="noopener noreferrer"&gt;Design choices made by LLM-based test generators prevent them from finding bugs&lt;/a&gt;. 2024.&lt;/li&gt;
&lt;li&gt;Cui, Y. &lt;a href="https://arxiv.org/abs/2505.09027" rel="noopener noreferrer"&gt;Tests as Prompt: A Test-Driven-Development Benchmark for LLM Code Generation (WebApp1K)&lt;/a&gt;. 2025.&lt;/li&gt;
&lt;li&gt;Yu, H., Li, K., et al. &lt;a href="https://arxiv.org/abs/2608.16742" rel="noopener noreferrer"&gt;TDD-Agent: Test-Driven Reasoning for Code Generation&lt;/a&gt;. 2026.&lt;/li&gt;
&lt;li&gt;Hu, Y., Jiang, N., et al. &lt;a href="https://arxiv.org/abs/2509.24148" rel="noopener noreferrer"&gt;TENET: Leveraging Tests Beyond Validation for Code Generation&lt;/a&gt;. 2025.&lt;/li&gt;
&lt;li&gt;Fakhoury, S., Naik, A., Sakkas, G., Chakraborty, S., Lahiri, S. K. &lt;a href="https://arxiv.org/abs/2404.10100" rel="noopener noreferrer"&gt;LLM-Based Test-Driven Interactive Code Generation: User Study and Empirical Evaluation&lt;/a&gt;. IEEE TSE, 2024.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>ai</category>
      <category>tdd</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
