<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Optivem Journal: Test Driven Development]]></title><description><![CDATA[Legacy Code is a nightmare to maintain. You tried TDD, but it didn’t work. TDD in Legacy Code seems impossible. That’s why I made the TDD in Legacy Code series.]]></description><link>https://journal.optivem.com/s/test-driven-development</link><image><url>https://substackcdn.com/image/fetch/$s_!0CjJ!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fbucketeer-e05bbc84-baa3-437e-9518-adb32be77984.s3.amazonaws.com%2Fpublic%2Fimages%2F9abead4c-3f54-46b1-96aa-7033849416df_200x200.png</url><title>Optivem Journal: Test Driven Development</title><link>https://journal.optivem.com/s/test-driven-development</link></image><generator>Substack</generator><lastBuildDate>Sun, 30 Aug 2026 05:34:57 GMT</lastBuildDate><atom:link href="https://journal.optivem.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Valentina Jemuović, Optivem]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[optivem@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[optivem@substack.com]]></itunes:email><itunes:name><![CDATA[Valentina Jemuović]]></itunes:name></itunes:owner><itunes:author><![CDATA[Valentina Jemuović]]></itunes:author><googleplay:owner><![CDATA[optivem@substack.com]]></googleplay:owner><googleplay:email><![CDATA[optivem@substack.com]]></googleplay:email><googleplay:author><![CDATA[Valentina Jemuović]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[TDD in Legacy Code - Contract Tests - Components]]></title><description><![CDATA[How can Frontend & Backend Teams find out that they can't communicate, instead of waiting for slower Acceptance Tests?]]></description><link>https://journal.optivem.com/p/tdd-in-legacy-code-contract-tests-components</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-in-legacy-code-contract-tests-components</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 14 Aug 2026 06:02:31 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fc44c50a-fd15-408f-ba60-845c1b32ded4_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>&#128197; Join me: </span><strong><a href="https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers">Clean Architecture for Backend Developers</a></strong><span> on Wed 26th Aug, 5:00 - 6:30 PM (CEST)</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers&quot;,&quot;text&quot;:&quot;&#127942; Register now&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers"><span>&#127942; Register now</span></a></p><div><hr></div><p><em>&#128274;Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply TDD in Legacy Code. This article is part of the <a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a> series. </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Component Tests are fast because they stub their dependencies</h2><p>Component Tests give us fast feedback by testing a component <strong>in isolation</strong>.</p><p>The Frontend Team stubs out the Backend in <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-frontend">Frontend Component Tests in Legacy Code</a>; the Backend Team stubs out the External System (the ERP) in <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-backend">Backend Component Tests in Legacy Code</a>.</p><h2>But your stubs might be wrong!</h2><p>When the Frontend stubs the Backend, it makes an assumption: <em>&#8220;</em><code>POST /api/orders</code><em> returns </em><code>201</code><em> with </em><code>{ orderNumber }</code><em>.&#8221;</em></p><p>But what if the Backend Team renames that field, or changes the status code, or moves the endpoint. What happens to the Frontend&#8217;s Component Tests?</p><p><strong>They stay green.</strong> They&#8217;re still testing against the stub which hadn&#8217;t changed &#8212; but the stub&#8217;s assumption is now wrong.</p><p>The bug doesn&#8217;t show up until the Frontend and the Backend actually run together &#8212; when the Acceptance Test fails.</p><h2>How to catch stub mismatch? Contract Tests</h2><p>The Frontend says:</p><blockquote><p>&#8220;This is what I expect from the Backend.&#8221;</p></blockquote><p>Pact <strong>turns that expectation into a contract</strong>.</p><p>The Backend then runs the contract against itself.</p><p>If the Backend no longer matches what the Frontend expects, <strong>the Backend build fails.</strong></p><p>You don&#8217;t write a new test from scratch. You take a Frontend Component Test you already have, and <strong>replace the stub with a contract</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!t7_r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!t7_r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 424w, https://substackcdn.com/image/fetch/$s_!t7_r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 848w, https://substackcdn.com/image/fetch/$s_!t7_r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 1272w, https://substackcdn.com/image/fetch/$s_!t7_r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!t7_r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png" width="1456" height="465" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:465,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:59975,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/210751980?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!t7_r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 424w, https://substackcdn.com/image/fetch/$s_!t7_r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 848w, https://substackcdn.com/image/fetch/$s_!t7_r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 1272w, https://substackcdn.com/image/fetch/$s_!t7_r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc587b8f5-09d6-4330-a578-7e3f9c2050a0_1943x620.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here are the steps to introduce Contract Tests in Legacy Code. You&#8217;ll get tasks to implement in your GitHub Sandbox Project. &#11015;&#65039;&#11015;&#65039;&#11015;&#65039;</p>
      <p>
          <a href="https://journal.optivem.com/p/tdd-in-legacy-code-contract-tests-components">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD - Stop Mocking JPA: You're Testing the Wrong Thing]]></title><description><![CDATA[Code Example]]></description><link>https://journal.optivem.com/p/tdd-stop-mocking-jpa-youre-testing-wrong-thing</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-stop-mocking-jpa-youre-testing-wrong-thing</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 17 Jul 2026 06:01:26 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/1c9112a7-5683-4773-a717-4d5de49ab6b7_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me: <strong><a href="https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers">Clean Architecture for Backend Developers</a></strong> on Wed 26th Aug, 5:00 - 6:30 PM (CEST)</p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>One of the biggest mistakes I see in Spring applications is developers mocking JPA.</p><p>When tests become slow, they replace the database with mocked <code>JpaRepository</code> interfaces.</p><p>The tests are faster.</p><p>But the architecture is now coupled to the ORM.</p><h2>Why Mocking JPA Is a Design Smell</h2><p>Developers mock JPA because they want:</p><ul><li><p>fast tests</p></li><li><p>no database</p></li><li><p>isolated business logic</p></li></ul><p>Those are good goals.</p><p>Mocking JPA isn&#8217;t.</p><p>Why?</p><ul><li><p>You&#8217;re mocking a framework you don&#8217;t own</p></li><li><p>Your tests depend on Spring Data instead of your own abstractions</p></li><li><p>Changing persistence forces you to change unit tests</p></li><li><p>Business logic stays coupled to the ORM</p></li></ul><p>The mock removes the database.</p><p>It doesn&#8217;t remove the dependency on the ORM.</p><h2>The Real Problem</h2><p>One team I worked with couldn&#8217;t upgrade their ORM for months because of a change to how inheritance was mapped.</p><p>The application wasn&#8217;t the problem.</p><p>The business logic wasn&#8217;t the problem.</p><p>The problem was that the ORM had leaked into the application layer and the tests.</p><p>A change in persistence rippled through the entire codebase.</p><p><strong>A concrete example:</strong></p><ul><li><p>In .NET, EF Core's Table-Per-Hierarchy inheritance auto-adds a <code>Discriminator</code> column, and its behavior has shifted across major versions &#8212; nullability, length, how it's configured and queried.</p></li><li><p>An upgrade changes the discriminator, and that change ripples into every piece of code that touches those entities.</p></li><li><p>JPA has the same trap: single-table inheritance auto-creates a <code>DTYPE</code> discriminator column, so a change to the mapping reaches straight into the application layer and its tests.</p></li></ul><h1>&#128161; Code Example</h1>
      <p>
          <a href="https://journal.optivem.com/p/tdd-stop-mocking-jpa-youre-testing-wrong-thing">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD in Legacy Code - Maintainable Component Tests - Backend]]></title><description><![CDATA[Many Backend Teams write unmaintainable Backend Component Tests - coupled to the Backend API and ERP. I'll show you how to refactor these brittle tests.]]></description><link>https://journal.optivem.com/p/maintainable-component-tests-in-legacy-code-backend</link><guid isPermaLink="false">https://journal.optivem.com/p/maintainable-component-tests-in-legacy-code-backend</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 02 Jul 2026 06:02:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/64035a71-683a-410a-9b9a-5d2279529b79_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><span>&#128197; Join me: </span><strong><a href="https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers">Clean Architecture for Backend Developers</a></strong><span> on Wed 26th Aug, 5:00 - 6:30 PM (CEST)</span></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers&quot;,&quot;text&quot;:&quot;&#127942; Register now&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers"><span>&#127942; Register now</span></a></p><div><hr></div><p><em>&#128274;Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply TDD in Legacy Code. This article is part of the <a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a> series. </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Backend Component Tests provide fast feedback</h2><p>We&#8217;ve seen in the previous article <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-backend">Backend Component Tests in Legacy Code</a>, that Component Tests can provide us with fast feedback. The Backend Team can test the Backend in isolation, by stubbing out External Systems (such as the ERP).</p><h2>But they can be a maintenance nightmare!</h2><p>In <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-backend">Backend Component Tests in Legacy Code</a>, we illustrated the &#8220;simplest&#8221; Backend Component Test.</p><p>The simplest way to write a Backend Component Test is to stub the External System inline (with WireMock), call the Backend API directly (with <code>WebTestClient</code>), and read the response by digging into raw JSON paths.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;1bdb7eb8-85cf-47a1-8d23-2a35567b29a0&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Test
void shouldCreateOrderWithTotalPrice() {
    // Arrange
    var productDto = new ProductDto(2.50);
    var productDtoJson = objectMapper.writeValueAsString(productDto);
    erpWireMockStub.stubFor(WireMock.get(&#8220;/products?sku=APPLE1001&#8221;)
        .willReturn(WireMock.aResponse()
            .withStatus(200)
            .withHeader(&#8220;Content-Type&#8221;, &#8220;application/json&#8221;)
            .withBody(productDtoJson)));

    var orderRequest = new OrderRequest(&#8220;APPLE1001&#8221;, 5);

    // Act &amp; Assert
    webTestClient.post()
        .uri(&#8220;/api/orders&#8221;)
        .contentType(MediaType.APPLICATION_JSON)
        .bodyValue(orderRequest)
        .exchange()
        .expectStatus().isCreated()
        .expectBody()
        .jsonPath(&#8220;$.totalPrice&#8221;).isEqualTo(12.5);
}</code></pre></div><p>The problem is that this test is coupled in three places at once: the <strong>ERP wire format</strong> (the WireMock stub), the <strong>Backend API endpoint</strong> (<code>webTestClient.post().uri("/api/orders")</code>), and the <strong>response </strong>(the raw <code>$.totalPrice</code> JSON path).</p><p>If the API endpoint changes, or the ERP&#8217;s wire format changes (a renamed field, a different status code), then many such tests may break, so we have to waste time fixing tests. The plumbing is also copy-pasted into every test.</p><h2>How to write maintainable Backend Component Tests?</h2><p>In this article, I&#8217;ll show you how to introduce layers of abstraction, i.e. Component Test Architecture, so that you spend much less time writing &amp; maintaining these tests.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4hma!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4hma!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!4hma!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!4hma!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!4hma!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4hma!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png" width="1456" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:243475,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/204517102?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4hma!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!4hma!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!4hma!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!4hma!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1c053a5b-134b-4952-8b38-3ed64779c40d_3886x1239.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here are the steps to introduce Maintainable Backend Component Tests in Legacy Code. You&#8217;ll get tasks to implement in your GitHub Sandbox Project. &#11015;&#65039;&#11015;&#65039;&#11015;&#65039;</p>
      <p>
          <a href="https://journal.optivem.com/p/maintainable-component-tests-in-legacy-code-backend">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD: Do NOT implement all behaviors at once]]></title><description><![CDATA[Code Demo]]></description><link>https://journal.optivem.com/p/tdd-do-not-implement-all-behaviors-at-once</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-do-not-implement-all-behaviors-at-once</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 05 Jun 2026 15:11:33 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/81685678-c359-46e2-a6b5-e6b51257fba0_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me: <strong><a href="https://optivem.thinkific.com/products/live_events/clean-architecture-for-backend-developers">Clean Architecture for Backend Developers</a></strong> on Wed 26th Aug, 5:00 - 6:30 PM (CEST)</p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>The mistake is to look at the finished use case &#8212; availability check, number generation, persistence, a response &#8212; and write all of it in one go.</p><p>That&#8217;s not TDD.</p><p>That&#8217;s writing the answer and then sprinkling tests on top.</p><p>TDD is incremental: write the <strong>simplest behavior that could possibly work</strong>, get it green.</p><p>The next failing test decides what gets added next. </p><h2>Behavior 1: a guest can reserve a room</h2><p>Requirement:</p><blockquote><p>&#8220;Guest can reserve a room.&#8221;</p></blockquote><p>Notice what&#8217;s <em>not</em> in that sentence yet: nothing about availability. So we don&#8217;t build availability checking. We build the smallest thing the requirement asks for &#8212; a reservation gets added, and we get a reservation number back.</p><h3><strong>&#128308; RED &#8212; Write the failing test</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;3a336087-7695-40b6-9884-3dd7ddf13547&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">class PlaceReservationTest {

    private FakeReservationRepository reservationRepository;
    private StubReservationNumberGenerator reservationNumberGenerator;
    private PlaceReservationUseCase placeReservationUseCase;

    @BeforeEach
    void setUp() {
        reservationRepository = fakeReservationRepository();            // fake &#8212; in-memory, query state back
        reservationNumberGenerator = stubReservationNumberGenerator();  // stub &#8212; canned number
        placeReservationUseCase = new PlaceReservationUseCase(
            reservationRepository, reservationNumberGenerator);
    }

    @Test
    void createsReservation() {
        reservationNumberGenerator.returns("RES-123");

        var placeReservationRequest = PlaceReservationRequest.builder()
            .guestId("guest-1")
            .roomId("room-101")
            .build();

        var response = placeReservationUseCase.execute(placeReservationRequest);

        assertThat(response.reservationNumber()).isEqualTo("RES-123");

        var reservation = reservationRepository.findByReservationNumber("RES-123");
        assertThat(reservation.guestId()).isEqualTo("guest-1");
        assertThat(reservation.roomId()).isEqualTo("room-101");
    }
}</code></pre></div><p>Notice what exists so far:</p><ul><li><p>repository to add the reservation</p></li><li><p>number generator so the test can pin the returned number (<code>RES-123</code>) deterministically.</p></li></ul><p>There is <strong>no </strong><code>AvailabilityGateway</code> &#8212; nothing requires one yet.</p><p>Look at the request, too: it only has a guest and a room &#8212; <strong>no dates</strong>. A real reservation obviously has dates, but no behavior here <em>uses</em> them yet, so they&#8217;re not in the request yet.</p><p>The requirement was &#8220;reserve a room,&#8221; nothing about <em>when</em>. You add <em>data</em> for the same reason you add code: when a test needs it, not before.</p><h3><strong>&#128994; GREEN &#8212; Make it pass with the simplest code</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f390ecd7-7aaf-46f5-a87a-61e75c0eb31e&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public class PlaceReservationUseCase {

    private final ReservationRepository reservationRepository;
    private final ReservationNumberGenerator reservationNumberGenerator;

    public PlaceReservationUseCase(ReservationRepository reservationRepository,
                                   ReservationNumberGenerator reservationNumberGenerator) {
        this.reservationRepository = reservationRepository;
        this.reservationNumberGenerator = reservationNumberGenerator;
    }

    public PlaceReservationResponse execute(PlaceReservationRequest request) {
        var reservationNumber = reservationNumberGenerator.next();

        reservationRepository.add(new Reservation(
            reservationNumber,
            request.guestId(),
            request.roomId()
        ));

        return new PlaceReservationResponse(reservationNumber);
    }
}</code></pre></div><p>Green. It always adds the reservation &#8212; it never checks anything &#8212; because no test has demanded a check yet.</p><p>This feels <em>too</em> simple, and that&#8217;s the point. You are not allowed to add the availability logic now. There&#8217;s no failing test pushing you there.</p><h3><strong>&#128309; REFACTOR &#8212; only if you see the need</strong></h3><p>With the test green, you now get a safe moment to improve the design &#8212; rename, extract, remove duplication &#8212; <em>without changing behavior</em>. You take it <strong>only if you actually see an improvement worth making</strong>; if nothing stands out, you skip it and move on.</p><p>Here the use case is a few straight-line statements with nothing to tidy, so there's nothing to do.</p><h2>Behavior 2: only if the room is available</h2><p>Now a new requirement arrives:</p><blockquote><p>&#8220;A room can only be reserved if it is available for the selected dates.&#8221;</p></blockquote><p><em>Now </em>we need to know if the room is available.</p><h3><strong>&#128308; RED &#8212; Write the failing test</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;d5993566-036f-48df-86ea-2b486e81eb50&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">    @Test
    void rejectsReservationWhenRoomIsUnavailable() {
        availabilityGateway
            .forRoom("room-101")
            .from("2026-07-10")
            .to("2026-07-12")
            .returnsUnavailable();

        var placeReservationRequest = PlaceReservationRequest.builder()
            .guestId("guest-1")
            .roomId("room-101")
            .from("2026-07-10")
            .to("2026-07-12")
            .build();

        assertThatThrownBy(() -&gt; placeReservationUseCase.execute(placeReservationRequest))
            .hasMessage("Room is not available");
    }</code></pre></div>
      <p>
          <a href="https://journal.optivem.com/p/tdd-do-not-implement-all-behaviors-at-once">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD: If Your Test Never Fails, It’s Broken]]></title><description><![CDATA[The RED step]]></description><link>https://journal.optivem.com/p/tdd-if-your-test-never-fails-its-broken</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-if-your-test-never-fails-its-broken</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 07 Apr 2026 06:02:18 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ec518127-dfa2-4e37-993b-d671b8e93624_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128075; <em>Hello, this is Valentina with the free edition of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Now you&#8217;ve written a test.</p><p>But does it fail?</p><p>If it doesn&#8217;t, you have no proof that your implementation makes a difference.</p><p>A test that always passes is worse than no test.</p><p>It gives you a false sense of security.<br>It hides bugs instead of exposing them.</p><p>That&#8217;s why the <strong>RED step matters</strong>.</p><p>You watch it fail.</p><p>That failure is evidence.</p><p>Later, when it turns GREEN, you know the code caused it &#8212; not luck, not a misconfigured test, not a false positive.</p><h2>&#9889;Can the Test Actually Fail?</h2><p>If you&#8217;ve seen it fail:</p><ul><li><p>It confirms the test is valid.</p></li><li><p>It means that the later GREEN step actually proves your code works.</p></li><li><p>It protects you from false positives &#8212; those sneaky tests that always pass, giving you a fake sense of correctness.</p></li></ul><p>TDD makes it unavoidable.</p><p>No RED? No trust in your code.</p><h2>Why Developers Skip It (And Regret It)</h2><p>It&#8217;s tempting to skip RED:</p><ul><li><p>&#8220;I know this code will work, so why watch it fail?&#8221;</p></li><li><p>&#8220;It&#8217;s faster to just implement and see GREEN.&#8221;</p></li></ul><p>But skipping RED is like installing a fire alarm that never goes off &#8212; you won&#8217;t know something&#8217;s broken until it&#8217;s too late.</p><p>Watching the test fail is the <strong>cheapest, fastest bug prevention you can get</strong>.</p><h2>RED Before GREEN</h2><p>TDD isn&#8217;t just about writing tests.</p><p>It&#8217;s about <strong>proof</strong>.</p><p>Proof that your test matters.<br>Proof that your code works.<br>Proof that the system behaves as expected.</p><p>You can take a step further with Acceptance Test&#8211;Driven Development (ATDD).</p><p>Instead of just asking:</p><blockquote><p>&#8220;Does my code work?&#8221;</p></blockquote><p>ATDD asks:</p><blockquote><p>&#8220;Does the system behave as the user expects?&#8221;</p></blockquote><p>It&#8217;s less about writing more tests and more about making sure everyone &#8212; Devs, QA, and Product Owners &#8212; agrees on what &#8220;done&#8221; really means.</p><p>The cool part? Fewer arguments about whether something is broken, fewer regression bugs, and a lot less late-night firefighting &#8212; because expectations are clear <strong>before you write a single line of code</strong>.</p><div><hr></div><p>&#128640; <strong>Join me for: <a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">ATDD &#8211; Acceptance Testing Workshop</a></strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop&quot;,&quot;text&quot;:&quot;Join the workshop &#8594;&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop"><span>Join the workshop &#8594;</span></a></p><p>Limited spots. Get <strong>100 EUR off with code EARLYBIRD100</strong></p><p></p>]]></content:encoded></item><item><title><![CDATA[Clean Code Is NOT Optional]]></title><description><![CDATA[It&#8217;s tempting to write code fast...]]></description><link>https://journal.optivem.com/p/clean-code-is-not-optional</link><guid isPermaLink="false">https://journal.optivem.com/p/clean-code-is-not-optional</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Wed, 01 Apr 2026 06:02:52 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e5f34d3e-bd39-4e7d-ad18-2cd0c5357885_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128075; <em>Hello, this is Valentina with the free edition of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>It&#8217;s tempting to write code fast and plan to clean it up later.</p><p>Skip the naming.<br>Duplicate a bit of logic.<br>Add &#8220;just one more if&#8221;.</p><h2>Messy Code Is a Tax</h2><p>Every change on top of messy code costs more:</p><ul><li><p><strong>Features take longer</strong> because you can&#8217;t trust what&#8217;s already there. You read more than you write, trying to figure out what the code actually does.</p></li><li><p><strong>Bugs hide</strong> in unclear abstractions and duplicated logic. You fix something in one place and miss the copy-pasted version three files over.</p></li><li><p><strong>Refactoring is painful</strong> because dependencies are tangled. You pull one thread and everything unravels.</p></li></ul><p>So you slow down. Every change gets harder. Every decision gets riskier. Every task takes longer than it should.</p><p>And the worst part? You <em>know</em> you should fix it, but now you can&#8217;t justify the time &#8212; because there&#8217;s a backlog of features waiting.</p><p>This is how codebases rot. Not from one bad decision, but from hundreds of small ones that compound.</p><blockquote><p>No matter how slow you are writing clean code, you will always be slower if you make a mess. &#8212; Uncle Bob</p></blockquote><p>Clean code may feel slower at first. But every line you write on top of it is faster, safer, and less stressful.</p><p>The hard truth: Messy code always costs more time than taking the time to do it right.</p><h2>But How Do You Keep Code Clean &#8212; and Keep It Working?</h2><p>Say you&#8217;ve written clean, well-structured code. You refactor a service, rename some methods, extract a class. The code reads well. You&#8217;re confident it&#8217;s correct.</p><p>But is it?</p><p>How do you know that the order flow still works end to end? That the payment integration didn&#8217;t break? That a customer can still check out?</p><p>You don&#8217;t &#8212; unless you have tests that verify <em>behavior</em>, not implementation.</p><h2>Clean Code Needs a Safety Net</h2><p>Unit tests are fast. They give you quick feedback and make refactoring feel safe.</p><p>But even the best unit tests have a blind spot.</p><p>They verify pieces in isolation. They mock the database, stub the API, skip the serialization. So when everything passes, you know your <em>unit</em> <em>logic</em> works &#8212; but you don&#8217;t know if the <em>system logic</em> works.</p><p>The gaps live at the boundaries: the database query that returns data in a different shape than you assumed. The HTTP layer that silently drops a field. The integration that worked in isolation but breaks when it meets real I/O.</p><p>Unit tests can&#8217;t catch these problems &#8212; not because they&#8217;re poorly written, but because that&#8217;s not what they&#8217;re designed to do.</p><p>Acceptance tests fill that gap. They exercise the system end-to-end, through the same boundaries your users hit:</p><p>A <strong>unit test</strong> says: &#8220;This function calculates the discount correctly.&#8221;</p><p>An <strong>acceptance test</strong> says: &#8220;When a customer applies a coupon at checkout, the total price is reduced and the order goes through.&#8221;</p><p>They&#8217;re slower than unit tests. You won&#8217;t run them on every keystroke. But they answer the question that unit tests can&#8217;t:</p><p><strong>Does this actually work for the user?</strong></p><h2>Clean Code Without Acceptance Tests Is a Risk</h2><p>Clean code keeps you moving fast. Unit tests ensure your refactoring doesn&#8217;t break the unit's logic. So why do you need anything else?</p><p>Because unit tests verify your code works <em>in isolation</em>. Acceptance tests verify your system works <em>in reality</em>.</p><p>You can have clean code, a full suite of passing unit tests, and still deploy a broken checkout flow &#8212; because the bug lives at a boundary that no unit test touches.</p><ul><li><p><strong>Clean code</strong> lets you move fast.</p></li><li><p><strong>Unit tests</strong> make sure the pieces work.</p></li><li><p><strong>Acceptance tests</strong> make sure the system works.</p></li></ul><p>One without the other will eventually slow you down.</p><p>You need all three.</p><h2>Where do you start?</h2><p>If you&#8217;re working in a greenfield project, you can build all three from day one.</p><p>But what about legacy code &#8212; where there are no tests, and the code is too messy to unit test?</p><p>You don&#8217;t start with unit tests. You can&#8217;t &#8212; the code isn&#8217;t structured for them yet.</p><p>You start with acceptance tests.</p><p>They don&#8217;t need clean code. They don&#8217;t need dependency injection or isolated modules. They test the system from the outside, through the same entry points your users hit. That means you can add them to any codebase, no matter how messy.</p><p>Once acceptance tests are in place, you have a safety net. Now you can refactor toward clean code and add unit tests &#8212; without breaking what already works.</p><p>Ready to put this into practice?</p><p>I&#8217;m running a hands-on <strong><a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">Acceptance Testing Workshop</a></strong> on May 27&#8211;28 (4 hours).</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop&quot;,&quot;text&quot;:&quot;Join the workshop &#8594;&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop"><span>Join the workshop &#8594;</span></a></p><p>Limited spots. Register now with the early bird discount - <strong>100 EUR off with code EARLYBIRD100</strong></p>]]></content:encoded></item><item><title><![CDATA[TDD & ATDD]]></title><description><![CDATA[In Microservice Architecture]]></description><link>https://journal.optivem.com/p/tdd-and-atdd-in-microservice-architecture</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-and-atdd-in-microservice-architecture</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 27 Mar 2026 07:02:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5beed640-9a0c-4081-b7db-37b6bcb7f5d7_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>When I first got into TDD, I thought I had it figured out.</p><p>I was reading Kent Beck, Martin Fowler&#8230; all of it just made sense.</p><p>I was really into clean code and unit tests.</p><p>I knew about e2e testing, but I saw it as QA&#8217;s job, not mine. Those tests were really badly written and constantly breaking. And to be honest, I saw it as boring and something that developers shouldn&#8217;t be doing.</p><p>I saw the whole CI/CD pipeline as a separate thing done by a DevOps engineer &#8212; not me.</p><div><hr></div><p>Still, no matter how many unit tests were written, management kept on complaining:</p><blockquote><p>&#8220;Why are there still so many bugs?&#8221;</p><p>&#8220;Why is everything taking so long?&#8221;</p><p>&#8220;I thought the last release fixed this!&#8221;</p><p>&#8220;Customers are frustrated &#8212; something has to change!&#8221;</p></blockquote><div><hr></div><p><strong>&#9889;Want to skip the pain and go straight to the solution?</strong></p><p><strong>Every Release Is a Nightmare</strong> doesn&#8217;t have to be your reality.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop&quot;,&quot;text&quot;:&quot;Join the workshop &#8594;&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop"><span>Join the workshop &#8594;</span></a></p><p>Limited spots. Register now with the early bird discount - <strong>100 EUR off with code EARLYBIRD100</strong></p><div><hr></div><h2>A Story That Stuck With Me</h2><p>Later, I stumbled across something Dave Farley shared. The teams were practicing TDD&#8230; they were lots of unit tests, all green &#8212; but the system was failing in QA environment, for possibly weeks. QA engineers constantly had to play catch-up with their e2e tests.</p><p>And that&#8217;s what got Dave Farley to develop acceptance tests.</p><div><hr></div><p>I realized I had to see the bigger picture.</p><p>That meant looking at the whole continuous delivery pipeline and understanding how all the pieces fit together.</p><p>In the commit stage, we&#8217;re doing TDD and writing unit tests (and component tests)&#8212; making sure each component behaves the way we expect.</p><p>But further down the pipeline, there&#8217;s the acceptance stage, where ATDD comes in &#8212; checking that all those pieces actually work together as a system.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!u0K5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!u0K5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 424w, https://substackcdn.com/image/fetch/$s_!u0K5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 848w, https://substackcdn.com/image/fetch/$s_!u0K5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!u0K5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!u0K5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png" width="900" height="1110" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1110,&quot;width&quot;:900,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:120470,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/191875686?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!u0K5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 424w, https://substackcdn.com/image/fetch/$s_!u0K5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 848w, https://substackcdn.com/image/fetch/$s_!u0K5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 1272w, https://substackcdn.com/image/fetch/$s_!u0K5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fea3036e5-1075-443d-a679-551a9bf49e30_900x1110.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h1>&#9889;TDD &amp; ATDD in Microservice Architecture</h1><h2>ATDD: Seeing the System as a Whole</h2><p>If you zoom out and look at the system&#8230; it starts with a user story.</p><p>And that story comes with <strong>acceptance criteria</strong> &#8212; the conditions that tell you: <em>did we actually deliver what the user needs?</em></p><p>Instead of just reading those criteria&#8230; we can turn each one into an <strong>acceptance test</strong>. It&#8217;s basically like 1:1 mapping.</p><p>And these tests are very different from unit tests.</p>
      <p>
          <a href="https://journal.optivem.com/p/tdd-and-atdd-in-microservice-architecture">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD: No Test? No Implementation.]]></title><description><![CDATA[Test the Requirement First]]></description><link>https://journal.optivem.com/p/tdd-no-test-no-implementation</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-no-test-no-implementation</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Mon, 23 Feb 2026 11:18:58 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fafbaf5b-cfb1-4fa2-a27a-cef3ba6a3000_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128075; <em>Hello, this is Valentina with the free edition of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Most devs start coding with only a hazy idea of what &#8220;done&#8221; really means.</p><ul><li><p>You assume requirements are clear.</p></li><li><p>You assume you understand the edge cases.</p></li><li><p>You assume QA will catch any gaps.</p></li></ul><p>TDD forces you to confront assumptions before you touch the code.</p><p><strong>It&#8217;s not about writing more tests.</strong><br><strong>It&#8217;s about clarity, assurance, and focus.</strong></p><p>When you know you can test a requirement, you know exactly what success looks like.</p><p>And that changes everything &#8212; from how you design your interface to how you structure your implementation to how fast you can deliver working software.</p><h2>&#9889;Can You Even Test the Requirement?</h2><p>Before you write code, TDD forces you to ask:</p><blockquote><p>Can I write a test for this requirement?</p></blockquote><p>If you can&#8217;t, something&#8217;s wrong.</p><p>Maybe:</p><ul><li><p>The requirement is vague.</p></li><li><p>There&#8217;s no concrete example.</p></li><li><p>Nobody knows what &#8220;done&#8221; really means.</p></li></ul><p>And here&#8217;s the uncomfortable truth:</p><p>If you can&#8217;t construct an example to verify the requirement&#8230;<br>&#8230; you&#8217;re about to implement something blind.</p><p>You might push code that looks correct.<br>It might even pass code reviews.<br>And yet&#8230; it may fail silently in production.</p><p>TDD flips the script.</p><p>The first checkpoint isn&#8217;t your implementation.<br>It&#8217;s the requirement itself.</p><p><strong>No test? No implementation.</strong></p><p>That simple rule prevents hours (or days) of wasted effort, wasted code, and wasted frustration.</p><h2>&#9889;TDD &amp; ATDD</h2><p>TDD is amazing at keeping <strong>your code correct</strong>, but it mostly protects <strong>your implementation</strong>.</p><p>ATDD (Acceptance Test&#8211;Driven Development) takes it one step further.</p><p>Instead of just asking:</p><blockquote><p>&#8220;Can I write a test that proves my code works?&#8221;</p></blockquote><p>ATDD asks:</p><blockquote><p>&#8220;Can I write a test that proves the system behaves as the user expects?&#8221;</p></blockquote><p>It shifts the focus from &#8220;my code works&#8221; to &#8220;the system works for everyone.&#8221;</p><ul><li><p>Requirements become executable specifications.</p></li><li><p>Developers, QA, and Product Owners are all aligned on what &#8220;done&#8221; really means.</p></li><li><p>Regression bugs drop, because everyone agrees on the expected behavior before a single line of code is written.</p></li></ul><p>Think of it this way:</p><ul><li><p>TDD protects the code.</p></li><li><p>ATDD protects the behavior.</p></li></ul><p>If you&#8217;re working on <strong>legacy systems</strong> or teams where QA keeps reporting regression bugs, ATDD can transform the process &#8212; turning vague requirements and tribal knowledge into tests that everyone trusts.</p><h2>&#9889;Release Without the Stress</h2><p>You know something is wrong with how testing is done.</p><p>This workshop is for engineers who refuse to accept that &#8220;this is just how it is.&#8221;</p><p><strong>Date &amp; Time:</strong> Wed Feb 25, 5-7PM (CET)<br><strong>Where:</strong> Live online<br><strong>Cost:</strong> &#8364;97 &#8212; (100% off for Optivem Journal paid members)</p><p><strong>&#128204;Spots are limited &#8211; Sign up here:</strong> <a href="https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b">Acceptance Testing (Live Training)</a></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b&quot;,&quot;text&quot;:&quot;Reserve Your Spot&quot;,&quot;action&quot;:null,&quot;class&quot;:&quot;button-wrapper&quot;}" data-component-name="ButtonCreateButton"><a class="button primary button-wrapper" href="https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b"><span>Reserve Your Spot</span></a></p><p>&#127873; <strong>Free for Paid Members </strong>- <a href="https://journal.optivem.com/p/discount">Claim your 100% discount</a><br>Not a member yet? <a href="https://journal.optivem.com/subscribe">Upgrade now</a> for immediate access and replays</p><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[TDD: Test the API, NOT the world]]></title><description><![CDATA[Write fast and deterministic tests]]></description><link>https://journal.optivem.com/p/tdd-test-the-api-not-the-world</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-test-the-api-not-the-world</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 13 Feb 2026 09:23:41 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c00c418e-f1bf-43e9-b73d-a4cfc213166f_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me for <strong><a href="https://optivem.thinkific.com/products/live_events/2026-02-25-acceptance-testing-live-training">Acceptance Testing (Live Training)</a></strong> on Wed 25th Feb (17:00 - 19:00 CET) <em>(100% discount for Optivem Journal members)</em></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Many years ago, I used to think every test had to touch everything &#8212; the database, other services, payment providers&#8230; you name it. </p><p>Then I realized: <strong>that&#8217;s inefficient, feedback loop is too slow. </strong>We need tests at different levels.</p><p>A user adds a product to their cart, maybe ten times while browsing, and then finally hits checkout, then later cancels their order.</p><p>That&#8217;s two very different use cases: <strong>AddItemToCart </strong>and <strong>PlaceOrder</strong>. And each requires different tests.</p><h2>The problem: Use Cases and the External World</h2><p>Most real-world use cases depend on something external:</p><ul><li><p>Databases</p></li><li><p>Message brokers</p></li><li><p>External API services</p></li></ul><p>For example, the PlaceOrder use case involves the DB (we need to save orders), it involves message brokers (we publish an event, so that the reporting service can register the newly placed order), and it involves external API services (we need to use PayPal for processing payment for the placed order).</p><p>The problem with testing use cases in this way, involving the external world are the following:</p><ul><li><p>The test is too slow due to the I/O</p></li><li><p>We might not be able to test various logic scenarios</p></li><li><p>The test might not be able to run if the external system is not available</p></li></ul><h2>The solution: Isolation from the External World</h2><p>In order to be able to test our use case in isolation from the external world:</p><ul><li><p><strong>User-side api:</strong> We test the use case via its driving (incoming) port, so that we bypass UI / REST API</p></li><li><p><strong>Server-side api:</strong> We &#8220;mock out&#8221; the driven (outgoing) ports, so that we&#8217;re excluding DB, external APIs, etc. In this way, the use case depends on interfaces of the external world, and doesn&#8217;t know (or care) about concrete implementations</p></li></ul><p>This keeps your core logic <strong>isolated and maintainable</strong>.</p><h2>Server-side API: Enter Test Doubles</h2><p>When we write <strong>unit tests</strong>, those server-side ports are replaced with <strong>in-memory test doubles</strong>:</p><ul><li><p>Repositories Test Doubles (e.g. Order Repository Test Double)</p></li><li><p>Gateway Test Doubles (e.g. Notification Test Double, Payment Test Double)</p></li></ul><p>There are different types of Test Doubles: Dummies, Fakes, Stubs, Spies, Mocks (see Fowler&#8217;s <a href="https://martinfowler.com/bliki/TestDouble.html">Test Double</a> article)</p><p>The test doubles:</p><ul><li><p>Live entirely in memory</p></li><li><p>Run ultra fast</p></li><li><p>Behave predictably</p></li></ul><p>Basically, they let your tests focus on <strong>business logic</strong>, not &#8220;what the database or external API does today.&#8221;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OHx9!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OHx9!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 424w, https://substackcdn.com/image/fetch/$s_!OHx9!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 848w, https://substackcdn.com/image/fetch/$s_!OHx9!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 1272w, https://substackcdn.com/image/fetch/$s_!OHx9!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OHx9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png" width="1025" height="1264" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1264,&quot;width&quot;:1025,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:154142,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/181080596?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OHx9!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 424w, https://substackcdn.com/image/fetch/$s_!OHx9!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 848w, https://substackcdn.com/image/fetch/$s_!OHx9!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 1272w, https://substackcdn.com/image/fetch/$s_!OHx9!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F660821c1-9a0c-4705-99b3-bff58d075339_1025x1264.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><em>Note: The above image is for a microservice, but a similar image can be applied to the frontend. Also, in case of monolithic architecture, similar image can be applied to the monolith.</em></p><h2>AddItemToCart vs PlaceOrder (Real-life Example)</h2>
      <p>
          <a href="https://journal.optivem.com/p/tdd-test-the-api-not-the-world">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[SOLID vs TDD]]></title><description><![CDATA[Which One Actually Drives Design?]]></description><link>https://journal.optivem.com/p/solid-vs-tdd</link><guid isPermaLink="false">https://journal.optivem.com/p/solid-vs-tdd</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 29 Jan 2026 07:00:35 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d69fb555-fa43-4a67-80ae-d682b18f7f2f_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me for <strong><a href="https://optivem.thinkific.com/products/live_events/2026-02-25-acceptance-testing-live-training">Acceptance Testing (Live Training)</a></strong> on Wed 25th Feb (17:00 - 19:00 CET) <em>(100% discount for Optivem Journal members)</em></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>If you&#8217;ve ever sat in a Friday meeting, you&#8217;ve probably heard:</p><blockquote><p>&#8220;We need to follow SOLID!&#8221;<br>or<br>&#8220;TDD will guide our architecture!&#8221;</p></blockquote><p>But here&#8217;s the uncomfortable truth:</p><p><strong>Most developers treat SOLID like a rulebook and TDD like a silver bullet.</strong><br>And neither works that way.</p><p>The real question is: <strong>which one actually drives design?</strong></p><div><hr></div><h2>SOLID: The Smell Detector</h2><p>SOLID provides a set of principles for maintainable code. When you use SOLID principles to review code, you can notice design smells:</p><ul><li><p>Single Responsibility Principle: helps you notice volatile classes</p></li><li><p>Open/Closed Principle: helps you see where abstraction is needed</p></li><li><p>Liskov, ISP, DIP: point out awkward dependencies</p></li></ul><div><hr></div><h2>TDD: The Design Engine</h2><p>TDD (Test-Driven Development) helps us design in the following way:</p><ul><li><p>Red &#8594; write a failing test &#8594; this is forcing us to design the interface that exposes some behavior</p></li><li><p>Green &#8594; write the minimum code to pass &#8594; typically there is not much effort here regarding design, quick-and-dirty is fine</p></li><li><p>Refactor &#8594; we refactor code &#8212;&gt; clean design emerges</p></li></ul><p>Therefore, TDD leads to:</p><ul><li><p>Well-designed interfaces (in the RED step)</p></li><li><p>Well-designed implementation (in the GREEN step)</p></li></ul><h2>A Real-World Example: TDD First, SOLID After</h2>
      <p>
          <a href="https://journal.optivem.com/p/solid-vs-tdd">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD in Legacy Code - Maintainable Component Tests - Frontend]]></title><description><![CDATA[Many Frontend Teams write unmaintainable Frontend Component Tests - coupled to the UI. These tests are brittle. I'll show you how to refactor these tests to be maintainable, by using DSL & Drivers.]]></description><link>https://journal.optivem.com/p/maintainable-component-tests-in-legacy-code-frontend</link><guid isPermaLink="false">https://journal.optivem.com/p/maintainable-component-tests-in-legacy-code-frontend</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 08 Jan 2026 07:01:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0f807a71-e6ab-4b37-8cd2-2ca2a3a195c7_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me for <strong><a href="https://optivem.thinkific.com/products/live_events/2026-02-25-acceptance-testing-live-training">Acceptance Testing (Live Training)</a></strong> on Wed 25th Feb (17:00 - 19:00 CET) <em>(100% discount for Optivem Journal members)</em></p><div><hr></div><p><em>&#128274;Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply TDD in Legacy Code. This article is part of the <a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a> series. </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Frontend Component Tests provide fast feedback</h2><p>We&#8217;ve seen in the previous article, <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-frontend">Frontend Component Tests in Legacy Code</a>, that Frontend Component Tests can provide us with fast feedback. The Frontend Team can test Frontend in isolation, by stubbing out the Backend.</p><h2>But they can be a maintenance nightmare!</h2><p>In <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-frontend">Frontend Component Tests in Legacy Code</a>, we illustrated the &#8220;simplest&#8220; Frontend Component Tests. But unfortunately, they are unmaintainable.</p><p>The simplest way to write a Frontend Component Test is to locate UI elements via Text, Role, ID, etc., then trigger some actions on those UI elements. For example: <em>(pseudocode)</em></p><pre><code><code>  const skuInput = screen.getByLabelText('SKU');
  fireEvent.change(skuInput, { target: { value: 'APPLE1001' } });</code></code></pre><p>The problem with these kinds of tests, is that if there is UX/UI Redesign, or some UI refactoring, that many such tests may break, so we have to waste time fixing tests.</p><h2>How to write maintainable Frontend Component Tests?</h2><p>In this article, I&#8217;ll show you how to introduce layers of abstraction, i.e. Component Test Architecture, so that you spend much less time writing &amp; maintaining these tests.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nYqK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nYqK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nYqK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png" width="1456" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:null,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:null,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:null,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nYqK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here are the steps to introduce Maintainable Frontend Component Tests in Legacy Code. You&#8217;ll get tasks to implement in your GitHub Sandbox Project. &#11015;&#65039;&#11015;&#65039;&#11015;&#65039;</p>
      <p>
          <a href="https://journal.optivem.com/p/maintainable-component-tests-in-legacy-code-frontend">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD: Unit Tests - Backend (user-side API)]]></title><description><![CDATA[Unit tests define *what* are the outcomes of business logic, not *how* it is implemented.]]></description><link>https://journal.optivem.com/p/tdd-unit-tests-backend-user-side-api</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-unit-tests-backend-user-side-api</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 12 Dec 2025 07:01:42 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d7977189-e8ee-4e1c-8660-ebfdf6b6a832_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128073; Reserve Your Spot for the <strong><a href="https://atdd.optivem.com/">2026 ATDD Accelerator</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>When practicing TDD in Hexagonal Architecture, unit tests should target use cases - not internal classes, not frameworks, and not implementation details.</p><p>In other words: our tests interact with the business logic as an external observer.</p><h2>Unit Tests Target the User-Side API</h2><p>Unit tests talk only to the user-side API, which represents the component&#8217;s use cases:</p><ul><li><p><code>AddItemtoCart</code></p></li><li><p><code>RemoveItemFromCart</code></p></li><li><p><code>ViewCartContents</code></p></li><li><p><code>CalculateCartTotal</code></p></li><li><p><code>FilterCartItems</code></p></li><li><p><code>GetProductPrice</code></p></li></ul><p>The tests know nothing about:</p><ul><li><p><strong>Internal classes</strong><br>(e.g. <code>Cart</code>, <code>CartItem</code>, <code>PricingCalculator</code>, <code>InventoryChecker</code>)</p></li><li><p><strong>Frameworks</strong><br>(e.g. Spring, Hibernate, Express, NestJS)</p></li><li><p><strong>Persistence</strong><br>(e.g. SQL queries, ORM mappings, database schemas)</p></li><li><p><strong>Network calls</strong><br>(e.g. HTTP requests to inventory or payment services)</p></li><li><p><strong>Technical plumbing</strong><br>(e.g. transactions, caching, logging, retries)</p></li></ul><p>This is great because it means that we can do refactoring in a safe way.</p><p>As long as the use case behaves the same from the outside, we can:</p><ul><li><p><strong>Rename classes</strong><br>(e.g. <code>CartService</code> &#8594; <code>ShoppingCart</code>)</p></li><li><p><strong>Split logic</strong><br>(e.g. extracting pricing rules into a separate domain object)</p></li><li><p><strong>Merge logic</strong><br>(e.g. consolidating duplicate validation code)</p></li><li><p><strong>Move code around</strong><br>(e.g. shifting logic from a service into a domain entity)</p></li><li><p><strong>Change implementations entirely</strong><br>(e.g. in-memory storage &#8594; database, sync calls &#8594; async events)</p></li></ul><p>&#8230;and our tests remain green &#9989;</p><h2>Use Case First, Implementation Later</h2><p>In TDD, we write a unit test for a specific use case (the test describes behavior in business terms). Only then do we implement the use case (classes and code emerge naturally).</p><p>We focus purely on:</p><ul><li><p><strong>Inputs</strong><br>(e.g. <code>customerId</code>, <code>sku</code>, <code>quantity</code>, passed to <code>AddItemToCart()</code>)</p></li><li><p><strong>Outputs</strong><br>(e.g. cart contains the correct items and quantities, total price is calculated correctly)</p></li><li><p><strong>Observable side effects</strong><br>(e.g. an exception is thrown when adding an unavailable product, the cart is persisted via the repository interface)</p></li></ul><p>This:</p><ul><li><p><strong>Prevents premature design</strong><br>(e.g. you don&#8217;t design <code>CartItem</code> or <code>PricingCalculator</code> before knowing what behavior is required)</p></li><li><p><strong>Avoids over-engineering</strong><br>(e.g. you don&#8217;t implement caching, events, or complex inventory logic before it&#8217;s needed)</p></li><li><p><strong>Forces the design to stay aligned with real usage</strong><br>(e.g. the system only supports adding/removing items, calculating totals, and viewing the cart exactly as users would do, rather than unnecessary internal implementation details)</p></li></ul><h2>Big Picture</h2><p>The test enters through the same door as a client of the user-side API. </p><p>Since we want the test to span the hexagon only, we then have to &#8220;mock out&#8220; the server-side API, e.g. using fake adapters.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!GaWW!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!GaWW!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 424w, https://substackcdn.com/image/fetch/$s_!GaWW!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 848w, https://substackcdn.com/image/fetch/$s_!GaWW!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 1272w, https://substackcdn.com/image/fetch/$s_!GaWW!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!GaWW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png" width="1456" height="942" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:942,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:247271,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/180991061?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!GaWW!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 424w, https://substackcdn.com/image/fetch/$s_!GaWW!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 848w, https://substackcdn.com/image/fetch/$s_!GaWW!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 1272w, https://substackcdn.com/image/fetch/$s_!GaWW!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fac8fa60c-6da1-4dfe-b923-8b257d250773_2118x1370.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><div><hr></div><h2>Real-Life Example: Ordering System</h2><p>For an e-commerce system, suppose we have a frontend and a backend. We model the backend using Hexagonal Architecture. Let&#8217;s see what the user-side API looks like on the backend:</p>
      <p>
          <a href="https://journal.optivem.com/p/tdd-unit-tests-backend-user-side-api">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD: High Coupling vs. Low Coupling Tests]]></title><description><![CDATA[When writing tests, it&#8217;s not enough to &#8220;just have tests.&#8221;]]></description><link>https://journal.optivem.com/p/tdd-high-coupling-vs-low-coupling-tests</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-high-coupling-vs-low-coupling-tests</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 14 Nov 2025 07:02:22 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/dc177dc7-951c-4b92-aa65-03a7067f49c2_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h3>&#128683; High coupling &#8211; Tests coupled to Structure &#10132; Fragile Tests</h3><p><strong>Do NOT write tests as specifications of your implementation.</strong></p><p>When your tests know too much about the system&#8217;s internals (specific classes, methods, or dependencies), the tests become tightly coupled to the implementation, i.e. the internal structure.</p><p>If you change an implementation class, the test will break. This kind of testing leads to fragile tests.</p><p>&#128269; Example symptoms:</p><ul><li><p>Tests instantiate internal classes directly</p></li><li><p>Tests depend on method names or call chains</p></li><li><p>Small refactorings cause test failures even when behavior hasn&#8217;t changed</p></li></ul><p>&#128165; The result:</p><ul><li><p>Refactoring becomes risky and slow</p></li><li><p>Developers fear touching the code</p></li><li><p>Tests no longer describe <em>behavior</em> &#8212; they describe <em>implementation details</em></p></li></ul><h3>&#9989; Low Coupling &#8211; Tests coupled to Behavior &#10132; Stable Tests</h3><p><strong>DO write tests as requirements of the system.</strong></p><p>Tests should communicate with the module&#8217;s public API and test the module&#8217;s external behavior. A module consists of a group of classes, where one class acts as the public class (exposes the module&#8217;s public API, it&#8217;s the entry point to the module) and the other classes are internal classes (they are implementation details of the module).</p><p>When our tests don&#8217;t know about implementation (treating it as a black box), we can change the internal implementation and we&#8217;re not going to break the tests.</p><p>We can do as much refactoring as we want, and the tests will not break.</p><p>&#9989; Characteristics:</p><ul><li><p>Tests verify the module&#8217;s behavior, not its internal implementation.</p></li><li><p>Internal classes can change freely without breaking tests.</p></li><li><p>You can refactor safely and move fast.</p></li></ul><p>&#128161; Example:</p><ul><li><p>Your tests call <code>OrderService.placeOrder()</code> instead of directly testing the underlying repository, entity, or validation class.</p></li><li><p>If you refactor <code>OrderService</code> or your validation class, the tests still pass &#8212; because behavior hasn&#8217;t changed.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!m-hu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!m-hu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 424w, https://substackcdn.com/image/fetch/$s_!m-hu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 848w, https://substackcdn.com/image/fetch/$s_!m-hu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 1272w, https://substackcdn.com/image/fetch/$s_!m-hu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!m-hu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png" width="1025" height="1167" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1167,&quot;width&quot;:1025,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:121949,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/178781503?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!m-hu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 424w, https://substackcdn.com/image/fetch/$s_!m-hu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 848w, https://substackcdn.com/image/fetch/$s_!m-hu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 1272w, https://substackcdn.com/image/fetch/$s_!m-hu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6dbe7e4c-c5c0-4f00-8a17-d0f747eb33e9_1025x1167.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Real-life Example: Ordering System</h2>
      <p>
          <a href="https://journal.optivem.com/p/tdd-high-coupling-vs-low-coupling-tests">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD in Legacy Code - Component Tests - Backend]]></title><description><![CDATA[How can Backend & Frontend teams get isolated feedback whether their part works, without waiting for the slow Acceptance Tests?]]></description><link>https://journal.optivem.com/p/component-tests-in-legacy-code-backend</link><guid isPermaLink="false">https://journal.optivem.com/p/component-tests-in-legacy-code-backend</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 23 Oct 2025 06:00:32 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/99bad525-4465-4fbe-b54e-0fa59d6e382f_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Testing the Frontend in isolation</h2><p>Previously, in <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-frontend">Frontend Component Tests in Legacy Code</a>, we saw that the Frontend team can get feedback whether the Frontend works in isolation, by stubbing out the Backend. This means they don&#8217;t have to wait for the Backend team.</p><h2>Testing the Backend in isolation</h2><p>Similarly, the Backend team can get feedback whether the Backend works in isolation, by stubbing out External Systems. Backend Component Tests are independent of Frontend Component Tests, hence can be written in parallel.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!SfIk!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!SfIk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!SfIk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!SfIk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!SfIk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!SfIk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png" width="1456" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:230831,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/172556948?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!SfIk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!SfIk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!SfIk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!SfIk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84876cb8-e92c-4130-b881-5ae3a40a6962_3886x1239.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here are the steps to introduce Backend Component Tests in Legacy Code. You&#8217;ll get tasks to implement in your GitHub Sandbox Project. &#11015;&#65039;&#11015;&#65039;&#11015;&#65039;</p>
      <p>
          <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-backend">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD is NOT about high code coverage]]></title><description><![CDATA[Code Coverage shows you what you haven't tested, not how good your tests are]]></description><link>https://journal.optivem.com/p/tdd-is-not-about-high-code-coverage</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-is-not-about-high-code-coverage</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 17 Oct 2025 06:01:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/c9931c86-c936-4360-b767-de3a83eb05d8_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>You can achieve 100% code coverage having tests with zero assertions!<br><br>TDD isn&#8217;t about having useless or illogical tests just for the sake of reaching a certain metric.<br><br>The main goal of TDD is that we&#8217;re driven by specifications, and that the code we produce is testable and aligned with specifications.<br><br>High code coverage is just a byproduct of TDD, but not the goal.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!pTJt!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pTJt!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 424w, https://substackcdn.com/image/fetch/$s_!pTJt!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 848w, https://substackcdn.com/image/fetch/$s_!pTJt!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 1272w, https://substackcdn.com/image/fetch/$s_!pTJt!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pTJt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png" width="1025" height="574" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:574,&quot;width&quot;:1025,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:136542,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/176143329?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pTJt!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 424w, https://substackcdn.com/image/fetch/$s_!pTJt!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 848w, https://substackcdn.com/image/fetch/$s_!pTJt!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 1272w, https://substackcdn.com/image/fetch/$s_!pTJt!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F53e150ca-e593-44d1-beca-6172d694f8c3_1025x574.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>100% code coverage, 0 assertions</h2><p>You can literally hit every line of code with your tests &#8212; and still have no validation of behavior. This is called a &#8220;zero assertion test&#8220;.</p>
      <p>
          <a href="https://journal.optivem.com/p/tdd-is-not-about-high-code-coverage">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD: 1 unit test ≠ 1 class]]></title><description><![CDATA[TDD misconception: The class is the unit of isolation. That's wrong! Instead, the behavior is the unit of isolation.]]></description><link>https://journal.optivem.com/p/tdd-one-unit-test-does-not-equal-one-class</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-one-unit-test-does-not-equal-one-class</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 03 Oct 2025 07:03:01 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f8bff7d7-ea46-4bdf-b33e-062b61f10c95_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Join the <strong><a href="https://atdd.optivem.com/">ATDD Accelerator waitlist</a></strong></p><div><hr></div><p><em>&#128274; Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>I thought that: class = unit of isolation</h2><p>When I first started practicing TDD, I thought that &#8220;unit&#8221; in Unit Test meant &#8220;class.&#8221; I&#8217;d write a test class for every production class and a test method for every method. I&#8217;d isolate the class under test by mocking everything it touched - even things that didn&#8217;t make sense to mock: </p><ul><li><p>Write a test class for each production class</p></li><li><p>Write a test method for each production method</p></li><li><p>Isolate the class under test by mocking out all its dependencies</p></li></ul><p>Indeed, this is the same thing that Uncle Bob said many people try to do:</p><blockquote><p>&#8220;Most people who are new to TDD&#8230; create a kind of one-to-one correspondence between the production code and the test code. For example, they may create a <em>test class</em> for every production code class. They may create <em>test methods</em> for every production code method.&#8221;</p><p>Of course this makes sense, <em>at first</em>. After all, the goal of any test suite is to test the elements of the system. Why wouldn&#8217;t you create tests that had a one-to-one correspondence with those elements? Why wouldn&#8217;t you create a test class for each class, and a set of test methods for each method? Wouldn&#8217;t that be the <em>correct</em> solution?</p><p>And, indeed, most of the books, articles, and demonstrations of TDD show precisely that approach. They show tests that have a strong structural correlation to the system being tested. So, of course, developers trying to adopt TDD will follow that advice.</p><p>&#8211; Uncle Bob (<a href="https://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html">TDD Harms Architecture</a>)</p></blockquote><h2>&#9888;&#65039; But &#8220;class = unit of isolation&#8221; is problematic!</h2><p>Let&#8217;s see what happens when you write a Unit Test Class for each Production Class, and when you write a Unit Test Method for each Production Method.</p><p>Let&#8217;s say you refactor your code. Recall that refactoring means changing structure, without changing observable behaviors:</p><blockquote><p>&#8220;Refactoring (noun): a change made to the internal structure of software to make it easier to understand and cheaper to modify without changing its observable behavior&#8221;</p><p>&#8211; Martin Fowler (<a href="https://martinfowler.com/bliki/DefinitionOfRefactoring.html">Definition of Refactoring</a>)</p></blockquote><p>Examples of refactoring:</p><ul><li><p>Changing the signature of a public method</p></li><li><p>Moving a public method from one class to another</p></li><li><p>Splitting a class into multiple classes</p></li></ul><p>In these cases of refactoring, you&#8217;ll end up with broken unit tests! Why? Because the unit tests are coupled to the structure of the code! The tight coupling leads to fragile tests:</p><blockquote><p>&#8220;The problem is &#8211; and I want you to think carefully about this next statement &#8211; <em>a one-to-one correspondence implies extremely tight coupling</em>.</p><p>Think of it! If the structure of the tests follows the structure of the production code, then the tests are inextricably coupled to the production code&#8230;</p><p>It, frankly, took me many years to realize this. If you look at the structure of FitNesse, which we began writing in 2001, you will see a strong one-to-one correspondence between the test classes and the production code classes&#8230;</p><p>And, of course, we experienced some of the problems that you would expect with such a sinister design. We had fragile tests. We had structures made rigid by the tests. We felt the pain of TDD. And, after several years, we started to understand that the cause of that pain was that we were not designing our tests to be decoupled.</p><p>&#8211; Uncle Bob (<a href="https://blog.cleancoder.com/uncle-bob/2017/03/03/TDD-Harms-Architecture.html">TDD Harms Architecture</a>)</p></blockquote><p>So what if the tests are fragile? Well, we end up with increased maintenance costs. Because when we are refactoring, the tests break, so we have double the effort:</p><ul><li><p>Effort of making the change in the code itself &#8212;&gt; this is necessary</p></li><li><p>Effort of &#8220;fixing&#8220; the broken test &#8212;&gt; this is a waste of time</p></li></ul><p>So then we don&#8217;t want to refactor! This leads to code rot. Unmaintainable code makes future code changes even more expensive!</p><p>To summarize, the belief that &#8220;class = unit of isolation&#8220; leads to higher maintenance costs and slower delivery.</p><h2>The alternative: &#8220;behavior = unit of isolation&#8220;</h2><p>In the above, we&#8217;ve seen the problem of structural coupling, where tests are coupled to the structure of code; for every class, there&#8217;s one test. This leads to fragile tests, causing higher maintenance costs (and slower delivery).</p><p>The alternative is behavioral coupling, where tests are coupled to behavior of the code; for every behavior, there&#8217;s one test. This leads to robust tests, causing reduced maintenance costs (and faster delivery).</p><p>This is exactly what Kent Beck remarked:</p><blockquote><p>&#8220;Tests should be coupled to the behavior of code and decoupled from the structure of code.&#8221;</p><p>&#8211; Kent Beck (<a href="https://x.com/KentBeck/status/1182714083230904320">Twitter</a>)</p></blockquote><p>Suppose that we have a class <code>BankAccount</code>, that exposes the <code>withdraw() </code>method. Internally, it calls classes <code>BalanceCalculator</code> and <code>FeeCalculator</code>. In that case, we could say that <code>BankAccount</code> is a &#8220;public&#8221; class, whereas <code>BalanceCalculator</code> and <code>FeeCalculator</code> are &#8220;internal&#8220; classes.</p><p>In cases of structural coupling, we write 1 Test Class for 1 Production Class:</p><ul><li><p><code>BankAccountTest</code> class for <code>BankAccount</code> class </p></li><li><p><code>BalanceCalculatorTest</code> class for <code>BalanceCalculator</code> class</p></li><li><p><code>FeeCalculatorTest</code> class for <code>FeeCalculator</code> class</p></li></ul><p>In case of behavioral coupling, we write a Test Class only for the &#8220;Public&#8220; Production Class (that exposed behavior) and we do NOT write any Test Classes for the &#8220;Internal&#8220; Production Classes (because they are just a structural implementation detail). So we just have:</p><ul><li><p><code>BankAccountTest</code> class for <code>BankAccount</code> class (because that&#8217;s the &#8220;public class&#8220;)</p></li></ul><p><em>Please note, in both cases, the code coverage will be the same.</em></p><p>In the case of behavioral coupling, if &#8220;internal&#8220; classes are refactored, it will not cause breakage to the tests, because tests are coupled only to the interface of the &#8220;public&#8220; class. So in this way, we reduce maintenance costs.</p><p>This means we are testing BankAccount, which traverses BalanceCalculator and FeeCalculator. We do NOT mock BalanceCalculator and FeeCalculator.</p><p><em>The only thing we&#8217;ll mock out are external dependencies that involve I/O (e.g. file system, database, network, etc.) or non-determinism (e.g. system clock).</em></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!DF5z!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!DF5z!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 424w, https://substackcdn.com/image/fetch/$s_!DF5z!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 848w, https://substackcdn.com/image/fetch/$s_!DF5z!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 1272w, https://substackcdn.com/image/fetch/$s_!DF5z!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!DF5z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png" width="582" height="683.069268292683" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1203,&quot;width&quot;:1025,&quot;resizeWidth&quot;:582,&quot;bytes&quot;:87579,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/174855269?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!DF5z!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 424w, https://substackcdn.com/image/fetch/$s_!DF5z!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 848w, https://substackcdn.com/image/fetch/$s_!DF5z!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 1272w, https://substackcdn.com/image/fetch/$s_!DF5z!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc9206dd1-0fad-4bea-970d-928d573add91_1025x1203.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>Real-life Example: Ordering System</h2>
      <p>
          <a href="https://journal.optivem.com/p/tdd-one-unit-test-does-not-equal-one-class">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[How to introduce ATDD in Legacy Code? (in 3 months)]]></title><description><![CDATA[You should stay with your Big Ball of Mud, and stay with your Messy Code. I'll explain why and what you can do instead.]]></description><link>https://journal.optivem.com/p/how-to-introduce-atdd-in-legacy-code-in-3-months</link><guid isPermaLink="false">https://journal.optivem.com/p/how-to-introduce-atdd-in-legacy-code-in-3-months</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Wed, 10 Sep 2025 06:18:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/665a5791-0b8a-4cad-85cf-49525519d605_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join 100+ engineers at my next FREE live event <strong><a href="https://maven.com/p/f39276/stop-breaking-production-atdd-in-legacy-code">Stop Breaking Production: ATDD in Legacy Code</a></strong> (Sep 24 at 5:00 PM CET)</p><div><hr></div><p><em>&#128274;Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply TDD in Legacy Code. This article is part of the <a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a> series.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>You&#8217;re stuck in maintaining a Legacy Product. There&#8217;s no tests, you&#8217;re relying on the slow Manual QA. Most of your team&#8217;s sprint time is spent in fixing regression bugs and reworking features. Business is angry why nothing is delivered.</p><p>Everyone accepts this as &#8220;it&#8217;s the way things are, there&#8217;s nothing we can do about it&#8220;.</p><h2>The &#8220;silver bullet&#8221; fallacies</h2><p>So your team got together, and they all acknowledge these problems:</p><ul><li><p>Delivery is too slow (and it keeps getting slower)</p></li><li><p>Too many regression bugs (and the bug count rises over time)</p></li></ul><p>But then, when it comes to how to solve the problem, this is where it starts getting really messy. Everyone has a different opinion of what&#8217;s the root cause, and what needs to be done first.</p><p>Some people see the problem with code and architecture:</p><blockquote><p>&#8220;AI is the solution to everything. If only we bought the next latest-and-greatest AI tool to write 90% of our code, then we&#8217;d be shipping fast.&#8220;</p><p>&#8220;We have an old tech stack. If only we moved to the next shiny programming language and the latest framework, then our problems would be solved.&#8220;</p><p>&#8220;The problem is that we have a monolith. If only we moved to microservices, then all our problems would be solved.&#8221;</p><p>&#8220;Our code is messy. We should focus on SonarQube reports. If only we had clean code, then all our problems would be solved.&#8220;</p></blockquote><p>Some people see that the problem is with our process of working:</p><blockquote><p>&#8220;The problem is our User Story tickets. If only the PO was writing more detailed tickets, then it would solve our problems. The PO is the root cause of all problems.&#8220;</p><p>&#8220;The problem is that we have too many Scrum meetings. If we eliminated those meetings, then developers would have more time to do real work.&#8221;</p><p>&#8220;The problem is JIRA, we have to spend too much time on the tickets. If only we moved to a simpler ticketing system, we&#8217;d have less wasted time.&#8220;</p><p>&#8220;The problem is we have a siloed organization and we&#8217;re not communicating well. If only we solved the people problem, then we&#8217;d have better delivery.&#8221;</p><p>&#8220;Our PR review process is not good enough. Let&#8217;s write a procedure on how to do it better. We also need to have multiple reviewers.&#8220;</p><p>&#8220;If only we had well-documented coding standards, then developers would do more quality work.&#8220;</p></blockquote><p>Some people see the problem as we&#8217;re understaffed:</p><blockquote><p>&#8220;We don&#8217;t have enough developers. If we hired more developers, then things would speed up.&#8220;</p><p>&#8220;We don&#8217;t have enough QA Engineers. If we hired more QA Engineers, then it would reduce testing time.&#8220;</p></blockquote><p>Some people see the problem as being that we don&#8217;t have tests:</p><blockquote><p>&#8220;Let&#8217;s automate E2E Tests. We could buy an AI tool to do that. Then we could get rid of Manual QA.&#8221;</p><p>&#8220;If we wrote Unit Tests and had 80% code coverage, then we wouldn&#8217;t have these bugs. It&#8217;s important to catch bugs early.&#8220;</p></blockquote><p>Some give up, and say the only solution is to start from scratch:</p><blockquote><p>&#8220;Everything is horrible. We should just fire everyone, hire 10X developers and rewrite everything from scratch.&#8220;</p></blockquote><h2>Do NOT start with Architecture Redesign.</h2><p>You try to redesign Architecture, for example, by making your Monolith modular. Or you try to switch from the Monolith to Microservices. Or you want to redesign your Microservices to escape the Distributed Monolith. Or you want to restructure your database to support a modular system. Because you heard &#8220;modular&#8220; means good.</p><p>&#10060; If you try Architecture Redesign, <strong>BUT</strong> you do <strong>NOT</strong> have Acceptance Tests (instead you&#8217;re relying on Manual QA), then you&#8217;re going to spend months fixing regression bugs caused by your &#8220;better&#8220; architecture <em>(and your architecture redesign branch redesign won&#8217;t ever be ready for production)</em>. You&#8217;ll end up with cleaner architecture, but it doesn&#8217;t work correctly, and isn&#8217;t deployed to production. This is negative ROI.</p><p><strong>&#9989; The right way: Stay with your Big Ball of Mud (for now).</strong></p><h2>Do NOT start with Clean Code &amp; Unit Tests.</h2><p>You&#8217;ve probably heard Uncle Bob promoting Clean Code &amp; Refactoring everywhere. SonarQube is reporting lots of code smells, so you have to fix them, right?</p><p>&#10060; If you try to refactor towards Clean Code, without Unit Tests, then you&#8217;ll cause regression bugs. So then it means you have to introduce Unit Tests first.  But since the existing codebase is a Big Ball of Mud (everything is tangled up, business logic is coupled to I/O), then it&#8217;s not Unit Testable, so then you have to break dependencies to introduce Unit Tests. As you break dependencies (for the purpose of ensuring Unit Testability), <strong>BUT</strong> you do <strong>NOT</strong> have Acceptance Tests, then you&#8217;ll introduce lots of Regression Bugs (and your &#8220;big refactoring&#8220; branches won&#8217;t ever be ready for production). You&#8217;ll end with cleaner code, that doesn&#8217;t work correctly, and isn&#8217;t deployed to production. This is negative ROI.</p><p><strong>&#9989; The right way: Stay with your Messy Code (for now).</strong></p><h2>So what should we do first? ATDD.</h2><p>It took me a decade to realize that the solution is to firstly introduce this:</p><ol><li><p>Pipeline</p></li><li><p>Acceptance Testing</p></li><li><p>ATDD</p></li></ol><p>When you have that foundation, then you&#8217;re ready for any other improvements. You&#8217;ll then be able to safely upgrade your Tech Stack, redesign your Architecture, introduce Unit Tests &amp; clean up your Code.</p><h2>How to introduce ATDD in 3 months?</h2><p>If you&#8217;re a Senior Software Engineer or Engineering Leader, who has a strong level of self-initiative, and you want to introduce ATDD in your Legacy Project with guidance rather than trial-and-error, then I can help you:</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://atdd-accelerator.optivem.com/apply&quot;,&quot;text&quot;:&quot;Apply for the ATDD Accelerator Program&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://atdd-accelerator.optivem.com/apply"><span>Apply for the ATDD Accelerator Program</span></a></p><p>Now I&#8217;m going to share with you my step-by-step for implementing ATDD in Legacy Code in 3 months, without disrupting your feature delivery:</p>
      <p>
          <a href="https://journal.optivem.com/p/how-to-introduce-atdd-in-legacy-code-in-3-months">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD in Legacy Code - Component Tests - Frontend]]></title><description><![CDATA[ATDD provides business-facing feedback, but the feedback loop is too long. That's why we need CTDD, which requires us to write Component Tests.]]></description><link>https://journal.optivem.com/p/component-tests-in-legacy-code-frontend</link><guid isPermaLink="false">https://journal.optivem.com/p/component-tests-in-legacy-code-frontend</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 04 Sep 2025 06:01:29 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/9e77875e-b8f7-42c0-b586-9bfe2aac2c97_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join me at the FREE live event <strong><a href="https://maven.com/p/f39276/stop-breaking-production-atdd-in-legacy-code">Stop Breaking Production: ATDD in Legacy Code</a></strong> (Sep 24 at 5:00 PM CET)</p><div><hr></div><p><em>&#128274;Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply TDD in Legacy Code. This article is part of the <a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a> series. </em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>ATDD provides business-facing feedback.</h2><p>Through our journey up to now in <em><a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a></em> series, we&#8217;ve introduced ATDD. We received major benefits from ATDD:</p><ul><li><p>We reached alignment between PO, Software Engineers &amp; QA Engineers, so that they have a shared understanding of requirements before Software Engineers start coding</p></li><li><p>Acceptance Tests provide us with an objective &#8220;Definition of Done&#8220; for User Stories &amp; Bug Tickets, and provide us with a real measure of progress</p></li><li><p>Software Engineers receive relatively fast feedback whether they satisfied Acceptance Criteria or whether they introduced Regression Bugs, they wait for the Pipeline&#8217;s Acceptance Stage (1hr) rather than waiting for Manual QA Engineers (days/weeks)</p></li><li><p>QA Engineers are able to bring much higher value to the team, (1) by contributing to team discussions to ensure good scenario coverage, as reflected in writing testable acceptance criteria, and (2) by being able to focus more on Exploratory Testing, Usability Testing (instead of repetitive Regression Testing)</p></li></ul><p>All the above, combined, means that we are able to deliver software safely &amp; quickly.</p><h2>But the ATDD feedback loop is too long.</h2><p>ATDD is excellent in providing us with business-facing feedback (did we satisfy requirements, or did we introduce regression bugs). However, we face the following issues due to the slow feedback loop:</p><ol><li><p>It takes a long time to get feedback for a new Acceptance Test. It may take several hours/days for all teams to finish their work, associated with making some Acceptance Test pass, e.g., the Frontend Team needed to make changes, and the Backend Team needed to make their changes. We have to wait for everyone to finish, and then we&#8217;re able to see the results of the Acceptance Test. This is a slow feedback loop (we had to wait hours/days).</p></li><li><p>If the Acceptance Test fails, we have no idea which team is at fault - is the problem with Frontend, or with the Backend? Or perhaps is there a problem of miscommunication between them, that their API expectations aren&#8217;t matching? So we have to do time-consuming troubleshooting, involving multiple teams.</p></li><li><p>When we fix regression bugs (because some Acceptance Tests failed), we have to wait for 1hr for the Acceptance Stage to finish, so that we can be sure that our fix is correct and that we didn&#8217;t break anything. <em>Similarly, when we do some refactoring, we have to we have to wait for 1hr for the Acceptance Stage to finish, so that we can be sure that our refactoring hasn&#8217;t broken anything.</em> Suppose some Acceptance Test(s) fail. We don&#8217;t know whether our commit caused the failure or some other interim commit from another team member caused the failure, so we may need to manually review/debug multiple commits to discover which commit(s) caused Acceptance Tests to fail.</p></li></ol><h2>That&#8217;s why we need CTDD.</h2><p>How to overcome the problems of ATDD?</p><p>We need an additional inner loop - Component Test Driven Development (CTDD). This means that each team internally would be able to specify (RED), implement &amp; verify (GREEN), and refactor (REFACTOR) their component, independently of other components. The benefits are:</p><ol><li><p>Our team doesn&#8217;t have to wait several hours/days for all other teams to complete their work for an Acceptance Test, but rather we get feedback in minutes whether our Component Test(s) associated with that Acceptance Test are passing, so we have assurance that our work is completed.</p></li><li><p>Our team doesn&#8217;t have to wait for a failing Acceptance Test to discover that we&#8217;ve introduced a bug. Instead, we&#8217;d most likely discover it through the Component Test. We can do troubleshooting within their team, no need to involve other teams.</p></li><li><p>When we fix a regression bug caused by our Component, or when we do refactoring, we can get fast feedback (minutes) whether or not the Component works correctly.</p></li></ol><h2>We need Component Tests to practice CTDD.</h2><p>A key skill in practicing CTDD is the ability to write Component Tests. That is why we first need to write some Component Tests last to gain practice, before we move on to CTDD.</p><p>In this article, we&#8217;ll show how to write Component Tests. Firstly, we&#8217;ll show Frontend Component Tests, and later we&#8217;ll also show Backend Component Tests.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nYqK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nYqK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nYqK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png" width="1456" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:230831,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/149954757?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nYqK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!nYqK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6a90eb85-5087-4289-8c9d-ddc919f47ec6_3886x1239.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Here are the steps to introduce Frontend Component Tests in Legacy Code. You&#8217;ll get tasks to implement in your GitHub Sandbox Project. &#11015;&#65039;&#11015;&#65039;&#11015;&#65039;</p>
      <p>
          <a href="https://journal.optivem.com/p/component-tests-in-legacy-code-frontend">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD in Legacy Code - Component Testable Architecture]]></title><description><![CDATA[Legacy Code might be tangled up so that it might not be Component Testable, which prevents us from introducing Component Tests & Contract Tests. We need Component Testable Architecture.]]></description><link>https://journal.optivem.com/p/component-testable-architecture-in-legacy-code</link><guid isPermaLink="false">https://journal.optivem.com/p/component-testable-architecture-in-legacy-code</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 29 Aug 2025 07:15:19 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d0abc5f1-0cae-472e-9bb3-7c9b97f38279_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128197; Join 100+ engineers at my next FREE live event <strong><a href="https://maven.com/p/f39276/stop-breaking-production-atdd-in-legacy-code">Stop Breaking Production: ATDD in Legacy Code</a></strong> (Sep 24 at 5:00 PM CET)</p><div><hr></div><p><em>&#128274;Hello, this is Valentina with a premium issue of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply TDD in Legacy Code. This article is part of the <a href="https://journal.optivem.com/p/tdd-in-legacy-code-outline">TDD in Legacy Code</a> series.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><h2>Legacy Code might not be Component Testable</h2><p>In <a href="https://journal.optivem.com/p/acceptance-testable-architecture-in-legacy-code">Acceptance Testable Architecture in Legacy Code</a>, we covered the problem where a System is not Acceptance Testable, whereby it&#8217;s statically coupled to External Systems, which included the following cases:</p><ul><li><p>Hard-coded connections to External HTTP APIs</p></li><li><p>Static calls to the System Clock and other non-deterministic sources</p></li><li><p>Static calls to complex external libraries</p></li></ul><p>Aside from the problem of static coupling between our System and External Systems, Legacy Code also often has the problem of static coupling within our System, between the Components themselves:</p><ul><li><p>Hard-coded connections from Frontend to Backend</p></li><li><p>Hard-coded connections between Microservices</p></li></ul><h2>We can&#8217;t introduce Component Tests</h2><p>To write Component Tests, we need to have Component Dependency Stubs. For example: (1) to write Component Tests for the Frontend, we need to be able to stub out the Backend, (2) to write Component Tests for Microservice #1, and assuming Microservice #1 depends on Microservice #2 and Microservice #3, we then need to stub out those dependent microservices.</p><p>We need Component Dependency Stubs because they enable us to test our Component in isolation.</p><p>At runtime, to switch between Real Component Dependencies and Component Dependency Stubs, we need Component Dependency Configurability.</p><h2>So we need to ensure Component Testable Architecture</h2><p>That&#8217;s why we need to make some changes to our source code to ensure Component Dependency Configurability.</p><p>But how do these changes&#8230; without risking the whole system?</p><p>Here are the steps for achieving Component Testable Architecture in Legacy Code. You&#8217;ll get tasks to implement in your GitHub Sandbox Project. &#11015;&#65039;&#11015;&#65039;&#11015;&#65039;</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ZkAi!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ZkAi!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!ZkAi!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!ZkAi!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!ZkAi!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ZkAi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png" width="1456" height="464" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:464,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:227636,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/170257076?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ZkAi!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 424w, https://substackcdn.com/image/fetch/$s_!ZkAi!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 848w, https://substackcdn.com/image/fetch/$s_!ZkAi!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 1272w, https://substackcdn.com/image/fetch/$s_!ZkAi!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1e5e0e31-49b6-464e-9239-b5a055372903_3886x1239.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div>
      <p>
          <a href="https://journal.optivem.com/p/component-testable-architecture-in-legacy-code">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[TDD - Microservice Testing]]></title><description><![CDATA[Many developers are writing integration tests the wrong way - they write Broad Integration Tests, which have similar limitations as E2E Tests - limited scenarios, brittle & fragile.]]></description><link>https://journal.optivem.com/p/tdd-microservice-testing</link><guid isPermaLink="false">https://journal.optivem.com/p/tdd-microservice-testing</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Mon, 25 Aug 2025 06:01:07 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3a5c6bca-f422-49fa-8c1b-96cfffee2217_1000x666.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>&#128640; Do you want stop regression bugs escaping to production? Join me - next FREE live event: &#8220;<a href="https://maven.com/p/f39276/stop-breaking-production-atdd-in-legacy-code">Stop Breaking Production: ATDD in Legacy Code</a>&#8221; (Sep 24 at 5:00 PM CET).</p><div><hr></div><p>&#128075; <em>Hello, this is Valentina with the free edition of the Optivem Journal. I help Engineering Leaders &amp; Senior Software Developers apply <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">TDD in Legacy Code</a>.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/subscribe?"><span>Subscribe now</span></a></p><div><hr></div><p>Don&#8217;t write broad integration tests to compensate for everything that unit tests can't do.</p><p>Broad integration tests are slow &amp; fragile. They have similar limitations as E2E Tests, because both traverse across dependencies.</p><p>Unfortunately, I&#8217;ve seen many teams write broad integration tests!</p><p>Instead of writing broad integration tests, you need tests at different levels:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xK5B!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xK5B!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 424w, https://substackcdn.com/image/fetch/$s_!xK5B!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 848w, https://substackcdn.com/image/fetch/$s_!xK5B!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 1272w, https://substackcdn.com/image/fetch/$s_!xK5B!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xK5B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png" width="1025" height="1274" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1274,&quot;width&quot;:1025,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:116195,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/170802698?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xK5B!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 424w, https://substackcdn.com/image/fetch/$s_!xK5B!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 848w, https://substackcdn.com/image/fetch/$s_!xK5B!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 1272w, https://substackcdn.com/image/fetch/$s_!xK5B!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c154705-1bb9-4171-a203-61e8cd2843fe_1025x1274.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h3>Narrow Integration Tests</h3><p>Narrow Integration Tests are used to verify infrastructure logic (Driven Adapters). For example, the Order Microservice may have these Narrow Integration Tests:</p><ul><li><p>Narrow Integration Test for the Order Repository - to verify database integration (e.g. using ORM, SQL, NoSQL, etc.), e.g. when we add entities, can we retrieve them <em>(note that the test connects to a real database via Testcontainers)</em></p></li><li><p>Narrow Integration Test for the Event Published - to verify messaging queue integration (e.g. RabbitMQ, Kafka), that we are correctly mapping our events to the data structures required by the messaging brokers <em>(note that the test connects to a real messaging queue via Testcontainers)</em></p></li><li><p>Narrow Integration Test for the Product Gateway - to verify integration with another microservice, e.g. with the Product Microservice - that we are correctly mapping our domain to the requests needed by Product Microservice, and whether we&#8217;re correctly interpreting the responses from the Product Microservice <em>(note that the test connects to a stubbed version of the Product Microservice, not the real thing)</em></p></li><li><p>Narrow Integration Test for the Payment Gateway - to verify integration with an External System, e.g. PayPal - that we are correctly mapping our domain to the requests needed by PayPal, and whether we&#8217;re correctly interpreting the responses from the PayPal <em>(note that the test connects to a stubbed version of the PayPal, not the real thing)</em></p></li></ul><p>Narrow Integration Tests are also used to verify Presentation Logic (Driving Adapters). For example, suppose that Order Microservice exposes a REST API - then, we verify that we can correctly translate HTTP requests into Driving Port Requests, and that we can correctly map Driving Port Responses into HTTP Responses.</p><h3>Component Tests</h3><p>Component Tests are used to test each component in isolation from other Components.</p><p>For example, suppose that the Order Microservice depends on Product Microservice and depends on PayPal. In that case, we want to test a flow through the Order Microservice, starting at the Order Microservice REST API, including the real database (via Testcontainers) but excluding Component Dependencies (we stub out the Product Microservice) and excluding External System Dependencies (PayPal). In this way, the Order Microservice team can get feedback on whether Order Microservice itself works correctly.</p><h3>Contract Tests</h3><p>In the case of integrating with dependencies, since we use stubs, we want to be sure that the stubs match the real thing.</p><p>In the case of testing Order Microservice, we stub out the Product Microservice and we stub out PayPal. How do we know whether the stubs are actually valid, that their API matches the API of the real things? We use Contract Tests:</p><ul><li><p>Contract Tests for the Product Microservice &amp; Product Microservice Stub provide assurance that the Product Microservice Stub exposes the same API that the Order Microservice relies on when communicating with the real Product Microservice Test Instance</p></li><li><p>Contract Tests for PayPal &amp; PayPal Stub provide assurance that the PayPal Stub exposes the same API that the Order Microservice relies on when communicating with the real PayPal Test Instance</p></li></ul><p>Ultimately, we get assurance that the Real Dependency API and the Dependency Stub API match.</p><h3>Acceptance Tests</h3><p>Acceptance Tests provide us with assurance that business requirements are satisfied. They answer the question: Does the system work correctly (in isolation from external systems)?</p><p>For example, for an eShop System, we can test whether placing order works correctly, e.g. is the correct order price calculated. We do this by stubbing out ERP (an External System) to specify product pricing, so that we can verify, for some SKU &amp; quantity, whether our eShop calculates the correct order total.</p><h3>Summary</h3><p>Testing at these levels helps you catch issues faster, keep tests maintainable, and have assurance you&#8217;re building the product right.</p><p>To learn more, read the <a href="https://journal.optivem.com/p/modern-test-pyramid-visual">Modern Test Pyramid</a>.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/p/tdd-microservice-testing/comments&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://journal.optivem.com/p/tdd-microservice-testing/comments"><span>Leave a comment</span></a></p><div><hr></div><h4>Want to apply TDD in practice?</h4><p>You tried TDD, but it didn&#8217;t work. That's why I'm going to help you practice TDD step-by-step. Apply TDD on a sandbox project. <a href="https://journal.optivem.com/p/tdd-in-legacy-code-transformation">Access TDD in Legacy Code.</a></p><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://journal.optivem.com/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">TDD &#183; Hexagonal Architecture &#183; Clean Architecture. Join 5K+ subscribers</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p>]]></content:encoded></item></channel></rss>