<?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]]></title><description><![CDATA[TDD | Hexagonal Architecture | Clean Architecture]]></description><link>https://journal.optivem.com</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</title><link>https://journal.optivem.com</link></image><generator>Substack</generator><lastBuildDate>Sun, 05 Apr 2026 12:04:14 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[Clean Architecture: DTOs Are NOT Just Wrappers]]></title><description><![CDATA[Code Example]]></description><link>https://journal.optivem.com/p/clean-architecture-dtos-are-not-just-wrappers</link><guid isPermaLink="false">https://journal.optivem.com/p/clean-architecture-dtos-are-not-just-wrappers</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 03 Apr 2026 06:01:42 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/983a1287-3fd1-45a2-8765-55c7ffe65c66_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>Developers like DTOs. And wrappers. And more wrappers.</p><p>Until your &#8220;simple&#8221; API layer turns into <strong>a tower of classes no one can read</strong>, tests become painful, and adding a new field feels like a chore.</p><p>DTOs and mappers are tools &#8212; not dogma. Clean Architecture doesn&#8217;t say &#8220;wrap everything 5 times.&#8221; It says: be intentional about what crosses boundaries.</p><h2>The Problem</h2><p>Many codebases follow a pattern like this:</p><pre><code><code>Domain objects
   &#8595;
Domain DTO
   &#8595;
Application DTO
   &#8595;
API DTO
   &#8595;
JSON</code></code></pre><p>That&#8217;s 3&#8211;4 layers of wrapping <strong>for the same data</strong>, plus mappers everywhere.</p><p>To be clear &#8212; sometimes multiple layers <em>are</em> justified. If you're serving both GraphQL and REST from the same domain, or maintaining a published API contract that must change independently from your domain, extra DTOs earn their keep. The problem isn't layers &#8212; it's layers that exist "just in case."</p><p><strong>The result:</strong></p><ul><li><p>Lots of boilerplate</p></li><li><p>Hard to understand what actually changes the domain</p></li><li><p>Fragile tests because every layer must be mocked</p></li><li><p>Accidental complexity: devs can&#8217;t tell which layer matters for business rules</p></li></ul><div><hr></div><p>&#128640; <strong>Register now</strong>: <a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">Acceptance Testing Workshop</a><br>Get 100 EUR off with code <strong>EARLYBIRD100</strong></p><div><hr></div><h1>&#128161;Code Example</h1><h2>Over-Wrapping Everything &#10060;</h2>
      <p>
          <a href="https://journal.optivem.com/p/clean-architecture-dtos-are-not-just-wrappers">
              Read more
          </a>
      </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 25&#8211;26 (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 role="img" 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><title></title><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[ATDD – How Do You Actually Start?]]></title><description><![CDATA[Write acceptance tests that catch real bugs]]></description><link>https://journal.optivem.com/p/atdd-how-do-you-actually-start</link><guid isPermaLink="false">https://journal.optivem.com/p/atdd-how-do-you-actually-start</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 24 Mar 2026 07:02:56 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e3f1f2e8-7c96-4355-9d4f-fc834f4a447e_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>Every time I write about acceptance testing, someone asks:</p><blockquote><p>This makes sense. But how do I actually start?</p></blockquote><p>I&#8217;ve gotten this in DMs. In comments. In conversations with Tech Leads.</p><p>They understand <em>why</em> acceptance tests matter.<br>They&#8217;ve felt the pain of shipping bugs.<br>They&#8217;ve read about ATDD.</p><p>But when they sit down to actually write one?</p><p>They stare at a blank file and think:</p><ul><li><p>Where do you put the acceptance tests?</p></li><li><p>How do you structure them so they don&#8217;t become as fragile as the E2E tests you&#8217;re trying to replace?</p></li><li><p>How do you design them so your whole team can write and maintain them &#8212; not just the one person who &#8220;gets testing&#8221;?</p></li></ul><div><hr></div><p>You want tests that are:</p><p><strong>Clean. Readable. Runs in seconds. Survives refactors.</strong></p><p>But&#8230;</p><ul><li><p>How do you build the DSL that makes it work?</p></li><li><p>How do you wire up the Drivers that connect these tests to your actual system?</p></li><li><p>How do you structure all of this so it scales across your whole team?</p></li></ul><div><hr></div><h2>Hands-On Acceptance Testing</h2><p>That&#8217;s why I built a live, hands-on workshop.</p><p>This isn&#8217;t a course you watch alone at 2x speed and forget by Friday.</p><p><strong>What you&#8217;ll learn:</strong></p><p><strong>1. The architecture.</strong> How acceptance tests are structured &#8212; DSL, Drivers, the layers that make them maintainable. You&#8217;ll understand <em>why</em> some acceptance tests become unmaintainable spaghetti and yours won&#8217;t.</p><p><strong>2. ATDD Cycle with AI.</strong> We&#8217;ll design an acceptance test together &#8212; from a business scenario all the way through to verification. You&#8217;ll get to see the layers (DSL &amp; Drivers) in practice.</p><p><strong>3. Apply it with your team.</strong> How to introduce acceptance testing into an existing project without stopping everything. How to get buy-in. How to start.<br></p><p><strong>When:</strong> May 25&#8211;26, 2026 | 2-4 PM CET <br><em>(FYI: May 27&#8211;28 </em>| <em>5-7 PM CET &#8212; Fully Booked)</em><br><strong>Where:</strong> Live on Zoom<br><strong>Duration:</strong> 4 hours (2 sessions x 2 hours)</p><p>&#128187; <strong>Who it&#8217;s for:</strong> Senior Engineers and Tech Leads who are tired of shipping bugs and ready to do something about it</p><p>&#128640; <strong>Register:</strong> <strong><a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">ATDD &#8211; Acceptance Testing Workshop</a><br>Get &#8364;100 off with code</strong> <strong>EARLYBIRD100</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mYju!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 424w, https://substackcdn.com/image/fetch/$s_!mYju!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 848w, https://substackcdn.com/image/fetch/$s_!mYju!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 1272w, https://substackcdn.com/image/fetch/$s_!mYju!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mYju!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png" width="1280" height="720" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:720,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:185160,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/190817885?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.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_!mYju!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 424w, https://substackcdn.com/image/fetch/$s_!mYju!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 848w, https://substackcdn.com/image/fetch/$s_!mYju!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.png 1272w, https://substackcdn.com/image/fetch/$s_!mYju!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb92ebee2-ad87-403e-93e4-709ea70fedaf_1280x720.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 role="img" 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><title></title><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>Don&#8217;t spend next month debugging something that should have been caught before it shipped.</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>&#8364;100 off with code EARLYBIRD100</strong></p>]]></content:encoded></item><item><title><![CDATA[The Architecture Behind Acceptance Tests That Don’t Break]]></title><description><![CDATA[How four layers turn fragile tests into executable specifications]]></description><link>https://journal.optivem.com/p/the-architecture-behind-acceptance-tests</link><guid isPermaLink="false">https://journal.optivem.com/p/the-architecture-behind-acceptance-tests</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 19 Mar 2026 07:02:47 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b6fda881-9471-4375-84a3-c79b75928fc9_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>This test doesn&#8217;t lie:</p><pre><code><code>scenario
    .given().product().withUnitPrice(20.00)
    .and().country().withCode("US").withTaxRate(0.08)
    .when().placeOrder().withQuantity(5).withCountry("US")
    .then().shouldSucceed()
    .and().order()
        .hasBasePrice(100.00)
        .hasTaxAmount(8.00)
        .hasTotalPrice(108.00);</code></code></pre><p>Clean. Readable. Runs in seconds. Survives refactors.</p><p>But how does it actually <em>work</em>?</p><p>What&#8217;s behind <code>.given().product().withUnitPrice(20.00)</code>? How does the same test run against both the API and the UI? Where does the &#8220;magic&#8221; come from?</p><p>I&#8217;m going to take this test apart, layer by layer, and show you exactly how the DSL is built.</p><div><hr></div><p><strong>&#9889;Ready to see this in action?</strong></p><p>I&#8217;m running a live, hands-on workshop where we build acceptance tests using this exact architecture. <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>The four layers</h2><p>An acceptance test DSL has four layers. Each layer has one job, and they only talk to the layer directly below them:</p><pre><code><code>Test           &#8594;  what we're testing (the scenario)
DSL            &#8594;  how we express it (the fluent API)
Driver Port    &#8594;  what we need from the system (interfaces)
Driver Adapter &#8594;  how we interact with the system (API calls, browser clicks)</code></code></pre><p>This separation is the entire reason acceptance tests are maintainable. Remove any layer and the whole thing falls apart.</p><div><hr></div><h2>Layer 1: The Test</h2><p>This is what you&#8217;ve already seen. A test method that reads like a business scenario:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;678b799c-be8a-40bc-9c92-5d0c0a1152ed&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@TestTemplate
@Channel({ChannelType.UI, ChannelType.API})
void shouldCalculateCorrectTotalWithTaxRate() {
    scenario
        .given().product()
            .withUnitPrice(20.00)
        .and().country()
            .withCode("US")
            .withTaxRate(0.08)
        .when().placeOrder()
            .withQuantity(5)
            .withCountry("US")
        .then().shouldSucceed()
        .and().order()
            .hasBasePrice(100.00)
            .hasTaxAmount(8.00)
            .hasTotalPrice(108.00);
}</code></pre></div><p>Notice what&#8217;s <em>not</em> here:</p><ul><li><p>No URLs</p></li><li><p>No HTTP calls</p></li><li><p>No CSS selectors</p></li><li><p>No database queries</p></li><li><p>No setup or teardown</p></li></ul><p>The test only knows <em>what</em> it&#8217;s testing. It has no idea <em>how</em> the system works under the hood. That&#8217;s the point.</p><p>The <code>@Channel</code> annotation is interesting &#8212; it tells the test runner to execute this same test once through the API and once through the UI. Same scenario, same assertions, two completely different ways of talking to the system. Here&#8217;s how it actually works.</p>
      <p>
          <a href="https://journal.optivem.com/p/the-architecture-behind-acceptance-tests">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Unit Tests passed. The bug shipped anyway.]]></title><description><![CDATA[The Pipeline was green. All the unit tests passed. All the E2E Tests passed. Yet the feature was broken.]]></description><link>https://journal.optivem.com/p/unit-tests-passed-the-bug-shipped</link><guid isPermaLink="false">https://journal.optivem.com/p/unit-tests-passed-the-bug-shipped</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 17 Mar 2026 07:02:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/8f43f40f-784c-4318-a654-30f46e384716_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>Marco stared at the Slack message from support.</p><p>&#8220;Customer says they were charged $110 on a $100 order. Tax should have been $8, not $10.&#8221;</p><p>He pulled up the dashboard. All tests green. CI/CD pipeline &#8212; clean. Not a single failure in weeks.</p><p>And yet, the tax calculation was wrong.</p><div><hr></div><p>Marco spent the next hour tracing the bug. The tax calculation itself was correct. The unit test proved it: pass in a rate, get back the right amount. No issues there.</p><p>The problem was simpler than that. And worse.</p><p>The developer who built the order service had hardcoded the tax rate as a constant &#8212; 10% &#8212; while working on the feature. The plan was to pull it from the country configuration later.</p><p>But &#8220;later&#8221; never came.</p><p>The feature shipped, the tests passed, and nobody noticed.</p><p>The unit test for the tax calculation? It tested the math. And the math was right. 10% of $100 is $10. Test passes.</p><p>But the <em>feature</em> was wrong. The customer was in a state with an 8% tax rate, and the system charged 10% because it never actually looked up the rate.</p><p>E2E tests? They had those too. But the E2E test just checked that the confirmation page appeared after placing an order. It never verified the actual total.</p><p><strong>The feature was broken. And no test caught it.</strong></p><div><hr></div><p>Here&#8217;s the uncomfortable truth most teams don&#8217;t talk about:</p><p><strong>Unit tests</strong> verify that individual components work. They can&#8217;t tell you whether the <em>feature</em> works. Marco&#8217;s tax calculation was correct &#8212; it just used the wrong input.</p><p><strong>E2E tests</strong> verify the happy path through the UI. But they&#8217;re slow, fragile, and they often check that <em>something</em> shows up on screen, not that the <em>right thing</em> shows up.</p><p>There&#8217;s a massive gap between &#8220;all my unit tests pass&#8221; and &#8220;this feature actually works as the customer expects.&#8221;</p><p>That gap is where your production bugs live.</p><div><hr></div><h2>There&#8217;s a test type that fills this gap</h2><p>It&#8217;s called an <strong>acceptance test</strong>.</p><p>Not another E2E test. Something fundamentally different.</p><p>An acceptance test is an executable specification of system behavior. It answers one question: <em>does this feature work the way the business expects?</em></p><p>Here&#8217;s what Marco&#8217;s E2E test looked like:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;f33e61f9-46d3-4427-a4d4-80449890cf4c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Test
void testPlaceOrder() {
    driver.get(baseUrl + "/products");
    driver.findElement(By.id("product-1")).click();
    driver.findElement(By.id("add-to-cart")).click();
    driver.findElement(By.id("quantity")).sendKeys("5");
    driver.findElement(By.id("checkout")).click();
    driver.findElement(By.id("place-order")).click();

    WebElement confirmation = driver.findElement(By.id("confirmation"));
    assertTrue(confirmation.isDisplayed());
}</code></pre></div><p>Brittle. Tied to the UI. Breaks if someone renames a button. And it only checks if the confirmation page shows up &#8212; it never verified the actual total.</p><p>Here&#8217;s the same scenario as an acceptance test:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;b587750e-fb39-4b9f-b945-4590bcf507a9&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">@Test
void shouldCalculateCorrectTotalWithTaxRate() {
    scenario
        .given().product()
            .withUnitPrice(20.00)
        .and().country()
            .withCode("US")
            .withTaxRate(0.08)
        .when().placeOrder()
            .withQuantity(5)
            .withCountry("US")
        .then().shouldSucceed()
        .and().order()
            .hasBasePrice(100.00)
            .hasTaxAmount(8.00)
            .hasTotalPrice(108.00);
}</code></pre></div><p>No browser. No CSS selectors. No flaky waits.</p><p>Just a clear business scenario: <em>given a product at $20, a quantity of 5, and a country with 8% tax &#8212; the base price should be $100, the tax should be $8, and the total should be $108.</em></p><p>This test would have caught Marco&#8217;s bug immediately. The system would return a total of $110 instead of $108 &#8212; test fails. The hardcoded constant would have been found before it ever reached a customer.</p><p>Marco&#8217;s team? After they introduced acceptance tests, customer-reported bugs dropped dramatically in the first few months. Not because they wrote more tests &#8212; but because they wrote the <em>right</em> tests.</p><div><hr></div><h2>Want to learn how to write tests like this?</h2><p>I&#8217;m running a live workshop: <strong><a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">Stop Shipping Bugs: Acceptance Tests Workshop</a></strong>.</p><p>4 hours. Two evenings. Live on Zoom, with me.</p><p>&#9889;I&#8217;ll walk you through the full architecture &#8212; DSL, Drivers, how it all fits together &#8212; and by the end, you&#8217;ll have written a real acceptance test from scenario to assertion.</p><p>&#128197; May 27-28, 5:00-7:00 PM CET</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><p>&#8212; Valentina</p>]]></content:encoded></item><item><title><![CDATA[SOLID: Stop Overloading Your Service Classes]]></title><description><![CDATA[Single Responsibility Principle (SRP)]]></description><link>https://journal.optivem.com/p/solid-stop-overloading-your-service-classes</link><guid isPermaLink="false">https://journal.optivem.com/p/solid-stop-overloading-your-service-classes</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 13 Mar 2026 07:02:11 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fa4244c3-4f7c-45d2-a26c-923a9ca65cca_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>Ever opened a class and thought, &#8216;What does this thing even do?&#8217;</p><p>It starts small, but over time&#8230; </p><h2>&#10060;One Class Tries to Do It All</h2><p><code>OrderService</code> becomes <strong>the place where everything goes</strong>.</p><p>Need to send an email? Add it to <code>OrderService</code>.<br>Need to generate an invoice? Add it to <code>OrderService</code>.<br>Need to call the shipping API? Add it to <code>OrderService</code>.</p><p>Suddenly, the class looks like this:</p><pre><code><code>OrderService

// Own reposponsibilities
- createOrder()
- cancelOrder()
- getOrderById()

// Extra responsibilities
- generateInvoice()
- shipOrder()
- sendEmail()
- trackOrder()

.... a laundry list of responsibilities...</code></code></pre><p>At first, it feels convenient.</p><p>Each method is small.<br>Each feature made sense when it was added.</p><p>But over time, this class becomes large, fragile, and difficult to change&#8230;</p><p>Because it&#8217;s responsible for too many different things:</p><ul><li><p>order logic</p></li><li><p>invoice generation</p></li><li><p>shipping logic</p></li><li><p>email notifications</p></li></ul><p>One class does <strong>all of it</strong>.</p><div><hr></div><p><strong>&#128161;Want to make it safe to redesign your architecture without breaking everything?</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. Early bird discount - <strong>100 EUR off with code EARLYBIRD100</strong></p><div><hr></div><h2>&#9989;One Class, One Job</h2><p>The Single Responsibility Principle (SRP) prevents this from happening.</p><p>A class should have <strong>one reason to change</strong>.</p><p>Not five. Not ten. One.</p><p>Instead of putting everything into <code>OrderService</code>, split responsibilities:</p><pre><code>OrderService
- createOrder()
- cancelOrder()
- getOrderById()

InvoiceService
- generateInvoice()

EmailService
- sendEmail()

ShippingService
- shipOrder()
- trackOrder()</code></pre><p>Now each class changes for <strong>one reason</strong>.</p><ul><li><p>Order logic changes &#8594; <code>OrderService</code></p></li><li><p>Invoice logic changes &#8594; <code>InvoiceService</code></p></li><li><p>Email logic changes &#8594; <code>EmailService</code></p></li><li><p>Shipping logic changes &#8594; <code>ShippingService</code></p></li></ul><p></p><p></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jQpH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jQpH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 424w, https://substackcdn.com/image/fetch/$s_!jQpH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 848w, https://substackcdn.com/image/fetch/$s_!jQpH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 1272w, https://substackcdn.com/image/fetch/$s_!jQpH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jQpH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png" width="604" height="726.5678048780488" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1233,&quot;width&quot;:1025,&quot;resizeWidth&quot;:604,&quot;bytes&quot;:132942,&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/185057081?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.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_!jQpH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 424w, https://substackcdn.com/image/fetch/$s_!jQpH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 848w, https://substackcdn.com/image/fetch/$s_!jQpH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.png 1272w, https://substackcdn.com/image/fetch/$s_!jQpH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb9c8abd5-2c5b-4191-b7e0-6a8ace4866d0_1025x1233.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 role="img" 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><title></title><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>&#128161;SRP Isn&#8217;t About One Method or Tiny Classes</h2>
      <p>
          <a href="https://journal.optivem.com/p/solid-stop-overloading-your-service-classes">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[“If you think good architecture is expensive, try bad architecture.”]]></title><description><![CDATA[(&#8212;Brian Foote and Joseph Yoder)]]></description><link>https://journal.optivem.com/p/if-you-think-good-architecture-is-expensive</link><guid isPermaLink="false">https://journal.optivem.com/p/if-you-think-good-architecture-is-expensive</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 10 Mar 2026 07:02:39 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/50e5a484-3d18-4de0-b1ac-32a10f4142f3_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><h2>Bad Architecture Is Expensive</h2><p>The <strong>&#8220;shortcut&#8221;</strong> decisions another developer made six months ago are now your daily headache.</p><p>Meetings start to revolve around workarounds. Every new feature is debated: &#8220;Is it safe? Will it break something else?&#8221;</p><p>And the pressure builds quietly.</p><p>Bad architecture doesn&#8217;t scream. It doesn&#8217;t send warning emails. It grows in the background.</p><p>It spreads through the system like rot.</p><p>Every service is <strong>tightly coupled</strong>. Every module is <strong>dependent on another</strong>. Every <strong>boundary unclear</strong>.</p><blockquote><p>&#8220;If you think good architecture is expensive, try bad architecture.&#8221;<br>&#8212; Brian Foote &amp; Joseph Yoder</p></blockquote><h2>So You Decide to Fix It</h2><p>You&#8217;ve had enough. You start redesigning. You decouple the modules. You clean up the boundaries. You refactor the mess left behind by someone else.</p><p>And then everything breaks.</p><p>Regression bugs everywhere. Features that worked yesterday are failing today. QA is overwhelmed. Stakeholders are asking what happened. Your manager wants answers.</p><p>And suddenly, you&#8217;re the problem. The person who tried to improve the system is now the one everyone blames for breaking it.</p><h2>What Went Wrong?</h2><p>It wasn&#8217;t the refactoring. It was what you didn&#8217;t have before you started.</p><p>Half your unit tests broke &#8212; not because the behavior changed, but because they were coupled to the old structure. The other half still passed, but it didn&#8217;t matter. Bugs were slipping through anyway, in the gaps between units that no test ever covered.</p><p>Your unit tests couldn&#8217;t tell you whether the system still worked. They were never designed to. Because <strong>unit tests</strong> only tell you if <strong>individual units of behavior work</strong>. They <strong>do</strong> <strong>NOT</strong> tell you if the <strong>system as a whole works</strong>.</p><p>Now QA has to catch everything. You push a change, they find a bug, you fix it, they test again&#8212;a loop that never ends. It&#8217;s slow, it&#8217;s expensive, it&#8217;s frustrating&#8230; and code still ships with bugs.</p><h2>Your System&#8217;s Real Safety Net</h2><p>This is exactly where acceptance tests change the game.</p><p>Acceptance tests don&#8217;t care about internal structure. They test what the system does from the outside &#8212; the behavior your users and stakeholders actually depend on.</p><ul><li><p>Redesign your modules? Acceptance tests should still pass.</p></li><li><p>Split a monolith into microservices? Acceptance tests should still pass.</p></li><li><p>Switch from SQL to NoSQL? Acceptance tests should still pass.</p></li></ul><p>In any System Redesign, Acceptance Tests should still pass, because we expect the system's black-box behavior NOT to change.</p><p>They&#8217;re the one safety net that survives architectural change.</p><h2>The Right Sequence</h2><p>Bad architecture and missing acceptance tests are an invisible tax. And the people paying for it are the ones writing, testing, and maintaining the code every day.</p><p>The fix isn&#8217;t to stop improving your architecture. It&#8217;s to change the order:</p><ol><li><p>First, write acceptance tests around what the system does today. Lock in the behavior. </p></li><li><p>Then redesign the architecture, knowing that if something breaks, you&#8217;ll see it immediately &#8212; not three sprints later in production.</p></li></ol><p>Acceptance tests first. Architecture second. That&#8217;s the sequence that actually works.</p><h2>See It in Practice</h2><p>Want to see how acceptance tests can save you time, frustration, and endless bug loops? I&#8217;m running a hands-on <strong><a href="https://optivem.thinkific.com/products/courses/2026-05-27-acceptance-testing-workshop">Acceptance Tests 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>&#8364;100 off with code EARLYBIRD100</strong></p>]]></content:encoded></item><item><title><![CDATA[Stop Writing Fragile Gherkin]]></title><description><![CDATA[The Gherkin maintenance nightmare and how to solve it]]></description><link>https://journal.optivem.com/p/stop-writing-fragile-gherkin</link><guid isPermaLink="false">https://journal.optivem.com/p/stop-writing-fragile-gherkin</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 06 Mar 2026 07:02:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/04d4e881-6a81-4643-b30e-770a52a748bf_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>If you&#8217;ve ever written acceptance tests in plain-text Gherkin, you probably had the same thought I did:</p><p>&#8220;This looks clean&#8230; so why is it so painful to maintain?&#8221;</p><p>Gherkin was supposed to let product owners write specifications.</p><p>But in reality?</p><p>Almost never happens.</p><p>Developers write the <code>.feature</code> files.<br>Developers maintain them.<br>Developers debug them at runtime.</p><p>Most teams follow the usual setup: they write Gherkin scenarios in <code>.feature</code> files using Cucumber (Java), SpecFlow (.NET) or Cucumber.js (TypeScript).</p><p>At first glance, it looks great:</p><p>English sentences.<br>Given / When / Then.</p><p>But&#8230; </p><h2>&#9888;&#65039;The Hidden Cost of Textual Gherkin</h2><p>Scenario files look readable, but become a maintenance burden:</p><pre><code><code>Scenario: Calculate base price as product of unit price and quantity
  Given a product with unit price $20.00
  When an order is placed for that product with quantity 5
  Then the order placement should succeed
  And the order base price should be $100.00</code></code></pre><p><strong>Duplication everywhere</strong></p><p>That sentence in the <code>.feature</code> file?</p><p>You repeat it again in step definitions (&#8220;glue code&#8220;).</p><p>Change one word?<br>Now you change it in multiple places:</p><ul><li><p>Change it in the step definition</p></li><li><p>Change it in all affected test scenarios</p></li></ul><p><strong>Typos = runtime failure</strong></p><p>Misspell a word?</p><p>You won&#8217;t know until you run the test.</p><p>The compiler doesn&#8217;t help you.</p><p>Your IDE doesn&#8217;t help you.</p><p>You only find out after everything compiles&#8230; and then fails.</p><p><strong>You&#8217;re on your own.</strong></p><p>No auto-complete.<br>No &#8220;what comes next?&#8221;<br>No guardrails.</p><p>You have to remember exact sentences.</p><p>And nothing prevents you from:</p><ul><li><p>Writing two <code>When</code> steps</p></li><li><p>Writing steps that don&#8217;t exist</p></li><li><p>Mixing domain concepts accidentally</p></li></ul><p>It&#8217;s fragile by design.</p><div><hr></div><p><strong>Want to skip the pain and go straight to the solution?</strong></p><p>I&#8217;m running a live workshop where we build acceptance tests that are compile-time safe, IDE-guided, and refactor-proof &#8212; no .feature files, no glue code.</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;:null}" data-component-name="ButtonCreateButton"><a class="button primary" 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>&#128161;What Changed Everything for Me</h2><p>Instead of writing English sentences and matching them with strings&#8230;</p>
      <p>
          <a href="https://journal.optivem.com/p/stop-writing-fragile-gherkin">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[DDD + Clean Architecture: Stop Putting Business Logic in the Application Layer]]></title><description><![CDATA[Your Clean Architecture isn&#8217;t clean if the domain is empty]]></description><link>https://journal.optivem.com/p/ddd-clean-architecture-dont-put-business-logic-in-application-layer</link><guid isPermaLink="false">https://journal.optivem.com/p/ddd-clean-architecture-dont-put-business-logic-in-application-layer</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 27 Feb 2026 07:01:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/e4338280-54a0-4076-9ab2-e0613f86fdee_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 people try Clean Architecture&#8230; they often end up with bloated application layers and empty domain layers.</p><pre><code><code>Application Layer:
   - Validate input
   - Load entities
   - Perform business logic
   - Call repositories
   - Call gateways
   - Publish events

Domain Layer:
   - Entity with getters and setters</code></code></pre><p>That&#8217;s an anemic domain, not a rich domain.</p><p>The <strong>domain doesn&#8217;t actually do anything</strong>. All the rules are in the application layer.</p><p>In Clean Architecture, we&#8217;re supposed to have a rich domain, which fits in with DDD.</p><h2>&#10060;Empty Domain</h2><p>If you&#8217;re used to CRUD, you probably see entities as simple data structures with public getters and setters.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;7171367a-d899-48c1-b8e3-74dc3dbae677&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">class Order {
    private int id;
    private List&lt;Item&gt; items;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public List&lt;Item&gt; getItems() {
        return items;
    }

    public void setItems(List&lt;Item&gt; items) {
        this.items = items;
    }
}</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;151262ff-fbf1-414f-94f2-6640ae6cbe20&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">class Item {
    private String sku;
    private int quantity;
    private boolean expired;

    public String getSku() {
        return sku;
    }

    public void setSku(String sku) {
        this.sku = sku;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public boolean isExpired() {
        return expired;
    }

    public void setExpired(boolean expired) {
        this.expired = expired;
    }
}</code></pre></div><p>Looks fine&#8230; but the business rules live elsewhere (application layer).</p><p>Your application service does this:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;java&quot;,&quot;nodeId&quot;:&quot;7d18ff13-2e49-4f84-9741-b01afe415658&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-java">public void addItemToOrder(Order order, Item item) {
    if (item.isExpired()) {
        throw new ExpiredException();
    }

    List&lt;Item&gt; items = order.getItems();
    if (items == null) {
        items = new ArrayList&lt;&gt;();
    }

    if (items.size() &gt; 10) {
        throw new LimitExceededException();
    }

    items.add(item);
    order.setItems(items);
    orderRepository.save(order);
}</code></pre></div><p>All the rules (expiration check, limit check) live in the <strong>application layer</strong>.</p><p>The entity is just a data container.</p><p>Basically, you&#8217;ve moved all the complexity into a layer that&#8217;s supposed to orchestrate, not decide.</p><h2>&#10060;Bloated Application Layer</h2>
      <p>
          <a href="https://journal.optivem.com/p/ddd-clean-architecture-dont-put-business-logic-in-application-layer">
              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[Hexagonal Architecture: Do NOT mock everything]]></title><description><![CDATA[Stop Mocking. Start Using Fakes, Stubs, and Spies.]]></description><link>https://journal.optivem.com/p/hexagonal-architecture-do-not-mock-everything</link><guid isPermaLink="false">https://journal.optivem.com/p/hexagonal-architecture-do-not-mock-everything</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 19 Feb 2026 07:01:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/5ba7bc75-490b-45ff-b8e9-0b7cd01a3dcd_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>The most common mistake in Hexagonal Architecture is mocking everything outside the domain.</p><p>Tests break after the tiniest change.</p><p>Refactors become nightmares.</p><p>And your &#8220;clean architecture&#8221; is now just a pile of mocks.</p><h2>&#10060; Over-Mocking Everything</h2><p>Okay, Order Service Test&#8230; let&#8217;s mock all the driven ports:  </p><pre><code><code>- Repository? mock
- Payment Gateway? mock
- Email Service? mock</code></code></pre><p>By the time your test runs, it&#8217;s fragile spaghetti:</p><ul><li><p>Change a method signature in the repository? Test fails.</p></li><li><p>Change a method signature in the payment gateway? Test fails.</p></li></ul><p>In the repository, I had a method <code>add(int id, OrderData data)</code>. I refactored it to be <code>add(Order order)</code>&#8230; tests fail because the mock is now out-of-date.</p><p>In the payment gateway, I used the method <code>getAllInvoices()</code> and then did in-memory filtering to find an invoice. In my source code I decided to use the method <code>getInvoice(String invoiceId)</code>&#8230; tests fail because the mock now is out-of-date.</p><p>You now have <strong>hexagonal architecture&#8230; and 47 failing tests</strong>.</p><p>Why it&#8217;s bad:</p><ul><li><p>Tests describe implementation, not behavior</p></li><li><p>Ports are treated as a checklist of mocks, not boundaries</p></li><li><p>Refactoring domain logic becomes a nightmare</p></li><li><p>Cognitive load goes through the roof</p></li></ul><h2>&#9989; Testing What Matters</h2><p>Focus on <strong>behavior, not mocks</strong>:</p><ul><li><p>&#9989; Fakes</p></li><li><p>&#9989; Stubs</p></li><li><p>&#9989; Spies</p></li><li><p>&#10060; Mocks</p></li></ul><p>For the same Order Service:</p><pre><code><code>- Repository &#8594; in-memory fake
- Payment Gateway &#8594; simple stub that can be configured to maybe payment succeed or fail
- Email Service &#8594; spy, whereby we collect emails in a queue, and inspect emails sent</code></code></pre><p>Notice the difference?</p><ul><li><p>Tests now describe what the domain does, not how infrastructure works.</p></li><li><p>Refactoring domain code rarely breaks tests.</p></li><li><p>Cognitive load drops &#8212; you&#8217;re testing one thing at a time.</p></li></ul><blockquote><p>Fake data, not behavior. Test behavior, not calls.</p></blockquote><h1>&#128161;Order Test Without Mock Overload</h1><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nfP-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nfP-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 424w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 848w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1272w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nfP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png" width="1456" height="1753" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1753,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:331079,&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/187517057?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.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_!nfP-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 424w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 848w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.png 1272w, https://substackcdn.com/image/fetch/$s_!nfP-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffab030e5-4a45-4ebe-824f-53ff8bbbf4af_1654x1991.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 role="img" 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><title></title><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/hexagonal-architecture-do-not-mock-everything">
              Read more
          </a>
      </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 role="img" 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><title></title><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[Developer vs QA: Regression Bug Ping-Pong]]></title><description><![CDATA[From &#8220;It worked&#8221; to &#8220;Nothing works as expected&#8221;]]></description><link>https://journal.optivem.com/p/developer-vs-qa-regression-bug-ping-pong</link><guid isPermaLink="false">https://journal.optivem.com/p/developer-vs-qa-regression-bug-ping-pong</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 10 Feb 2026 08:11:55 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3840ded2-df53-4a46-b689-6c07572225fb_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>You just finished a User Story.<br>Tested it locally. It worked.<br>You pushed it. Felt good.</p><p>And then&#8230;<br>QA comes back with <strong>a long list of regression bugs</strong>.</p><p>Again.</p><p>Your first reaction?</p><blockquote><p>&#8220;How is this even related to what I changed?&#8221;</p></blockquote><p>Your second reaction?</p><blockquote><p>&#8220;Did QA even test this properly?&#8221;</p></blockquote><p>And your third (the quiet one)?</p><blockquote><p>&#8220;Am I actually bad at my job?&#8221;</p></blockquote><p>You implemented <em>exactly</em> what the story asked for.<br>You didn&#8217;t refactor half the system.<br>You didn&#8217;t touch those areas.</p><h2>&#8220;But it worked on my machine.&#8221;</h2><p>You change one thing&#8230;<br>and accidentally affect five others.</p><p>Not because you&#8217;re reckless &#8212;<br>but because the system doesn&#8217;t <em>tell you</em> what behavior you&#8217;re about to break.</p><p>Manual QA discovers these issues <em>after the fact</em>.</p><p>The problem is that <strong>expected behavior lives only in people&#8217;s heads</strong>.<br>Or worse:</p><ul><li><p>In outdated tickets</p></li><li><p>In vague acceptance criteria</p></li><li><p>In tribal knowledge</p></li><li><p>In &#8220;that one person who knows the system&#8221;</p></li></ul><p>Nothing is executable.<br>Nothing runs automatically.<br>Nothing protects you <em>before</em> QA gets involved.</p><p>So regression bugs slip in &#8212; silently.</p><h2>What Acceptance Tests change</h2><p>Acceptance Tests move expectations <strong>out of conversations</strong> and <strong>into code</strong>.</p><p>They answer questions like:</p><ul><li><p>&#8220;What must <em>never</em> break?&#8221;</p></li><li><p>&#8220;What behaviors define &#8216;working&#8217;?&#8221;</p></li><li><p>&#8220;What does the business actually rely on?&#8221;</p></li></ul><p>And they answer them <strong>before</strong> QA ever sees the build.</p><p>When you have Acceptance Tests in your pipeline:</p><ul><li><p>You catch regression bugs immediately</p></li><li><p>QA spends less time reporting obvious breakages</p></li><li><p>Developers stop being surprised</p></li><li><p>Confidence comes back</p></li></ul><h2>Legacy Code is where bugs thrive</h2><p>You <em>expect</em> things to break.<br>You&#8217;re afraid to touch anything.<br>Every change feels risky.</p><p>If you&#8217;ve ever thought:</p><ul><li><p>&#8220;QA is blocking us&#8221;</p></li><li><p>&#8220;Regression bugs keep killing our velocity&#8221;</p></li><li><p>&#8220;Every release feels risky&#8221;</p></li><li><p>&#8220;We test, but it&#8217;s never enough&#8221;</p></li></ul><p>That&#8217;s exactly where Acceptance Tests help most.</p><h2>Want to see how this works in practice?</h2><p>&#128073; <strong>Join me for a 2-hour live training on Acceptance Testing</strong></p><p>I&#8217;ll walk you through:</p><ul><li><p>How to stop regression bugs <em>before</em> QA finds them</p></li><li><p>How to write maintainable, behavior-driven acceptance tests</p></li><li><p>How to do this safely &#8212; even in legacy code</p></li><li><p>How to reduce friction between devs and QA</p></li></ul><p>No theory-heavy fluff.<br>No &#8220;rewrite everything&#8221;.<br>Just a practical roadmap you can apply immediately.</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? &#128073; <a href="https://journal.optivem.com/subscribe">Upgrade now</a> for immediate access and replays</p>]]></content:encoded></item><item><title><![CDATA[8-Step Human–AI Code Review]]></title><description><![CDATA[AI-Powered Code Review]]></description><link>https://journal.optivem.com/p/8-step-human-ai-code-review</link><guid isPermaLink="false">https://journal.optivem.com/p/8-step-human-ai-code-review</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Fri, 06 Feb 2026 07:01:04 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/efcd24e5-39ff-4078-bb58-9fc52557150d_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>I used to love code reviews. Going through a merge request line by line, questioning naming, debating implementation choices, which design patterns to use, etc....</p><p>Fast forward to today. AI is everywhere. Developers use AI to write code, refactor code, write tests. Before, it used to take hours/days, now it&#8217;s reduced to minutes.</p><p>The problem? Just because AI is fast doesn&#8217;t mean the code is good. Or even maintainable. How much should humans actually review?</p><h2>Code Review in the age of AI</h2><p>A developer recently told me he&#8217;s stopped reviewing code the way he used to. </p><p>He doesn&#8217;t read every line anymore. Instead, he checks:</p><ul><li><p><strong>Is the code readable?</strong></p></li><li><p><strong>Can a human actually understand what&#8217;s going on?</strong></p></li></ul><p>If the answer is no, the developer should explain or improve it. That&#8217;s it.</p><h2>Human Review was already shrinking before AI</h2><p>Even <em>before</em> AI, human review was already shrinking.</p><p>Linters in the pipeline would do formatting checks. SonarQube would do clean code checks &#8212; cyclomatic complexity, duplication, smells. A lot of issues that reviewers used to flag was already handled automatically.</p><p>So the shift didn&#8217;t start with AI.<br>AI just took it one level further.</p><p>Before AI, SonarLint would flag code with:</p><ul><li><p>Code smells</p></li><li><p>Complexity</p></li><li><p>Duplication</p></li></ul><p>Developers fixed those issues locally.</p><p>Once the code reached the Pipeline, Linters &amp; SonarQube ran the checks again, and developers fixed any remaining problems before merging.</p><h1>8 Steps to Smarter Human&#8211;AI Code Reviews</h1>
      <p>
          <a href="https://journal.optivem.com/p/8-step-human-ai-code-review">
              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[Hotfix — Friday Afternoon]]></title><description><![CDATA[Why last-minute hotfixes ruin weekends &#8212; and how to avoid them.]]></description><link>https://journal.optivem.com/p/hotfix-friday-afternoon</link><guid isPermaLink="false">https://journal.optivem.com/p/hotfix-friday-afternoon</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 27 Jan 2026 07:01:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ef6576f1-af7b-43ad-87c7-56c0bbe2b81e_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 6 PM. You&#8217;ve already been working overtime, trying to fix the critical bug reported by a major customer at 3 PM.</p><p>Management is on an urgent call with the team &#8212; the fix MUST be deployed today. Every hour the software is down, the customer loses $10,000.</p><p>You know that it&#8217;s impossible to deliver the bug fix within that that time, but you&#8217;re powerless. You can&#8217;t do anything. You can&#8217;t tell the truth to the management.</p><p>The developers have to work overtime&#8230;. again&#8230;</p><h2>It&#8217;s not your fault&#8230;</h2><p>You tried to warn management that thorough QA was needed, but deadlines came first.</p><p>The release went ahead after just two manual QA cycles, because it was already late.</p><p>However, the whole team knew that there was not enough testing.</p><p>Customers became the testers. </p><h2>The pressure is on</h2><p>Important customers were reporting critical features not working. Management was pressuring developers to deliver a hotfix ASAP.</p><p>The fix must be deployed with the next few hours!</p><h2>We release the hotfix but we make the fire worse</h2><p>QA needed several days for thorough testing, but there wasn&#8217;t time.</p><p>The hotfix went out without proper regression testing&#8230; only to cause additional breakages in production. The customer was was angry because it took too long to deliver the hotfix, and even worse, that the hotfix caused additional major bugs!</p><h2>How do we avoid the stressful hotfixes?</h2><p>Many developers feel stuck, thinking that&#8217;s just the way it is, that development must be a high-stress job&#8230; there&#8217;s no other way.</p><p>There are two parts to the solution:</p><ol><li><p><strong>Prevent (or minimize) hotfixes </strong>&#8212; avoid the situation that customers are reporting regression bugs (esp. critical regression bugs)</p></li><li><p><strong>Handle inevitable hotfixes safely and quickly</strong> &#8212; deploy hotfix to production ASAP, without causing additional regression bugs</p></li></ol><h2>The solution: Acceptance Tests</h2><p>Acceptance Tests are most crucial test type your team needs to be sure that software works as required. By having Acceptance Tests in your Pipeline, you can release software with confidence, without waiting for customers to raise alarms.</p><p>When done right:</p><ul><li><p>Hotfixes become rare.</p></li><li><p>If a hotfix is needed, it can be deployed safely within hours.</p></li><li><p>You gain <strong>peace of mind</strong> and protect both your users and your team from unnecessary stress.</p></li></ul><h2><strong>What you&#8217;ll get out of this training</strong></h2><p>&#9989;&#8203; Walk through a practical <strong>Acceptance Testing Architecture</strong> using a real codebase</p><p>&#9989;&#8203; Learn the <strong>Four-Layer Model</strong> for Acceptance Tests and why it is maintainable</p><p>&#9989;&#8203; Write robust tests that express <strong>behavior in domain language</strong>, not UI or technical details</p><p>&#9989;&#8203; Make Acceptance Tests <strong>executable specifications</strong> shared by PO, QA, and Developers</p><h2><strong>Event Details</strong></h2><p>&#128197; <strong>Date &amp; Time:</strong> Wed Feb 25, 5-7PM (CET)<br>&#128205; <strong>Where:</strong> Live online<br>&#128176; <strong>Cost:</strong> &#8364;97 &#8212; (100% off for Optivem Journal paid members)</p><p>&#128279; <strong>Reserve Your Spot:</strong> <a href="https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b">Acceptance Testing (Live Training)</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Mmj3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!Mmj3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!Mmj3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!Mmj3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Mmj3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png" width="1200" height="675" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:675,&quot;width&quot;:1200,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:203736,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:&quot;https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b&quot;,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://journal.optivem.com/i/182183361?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.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_!Mmj3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 424w, https://substackcdn.com/image/fetch/$s_!Mmj3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 848w, https://substackcdn.com/image/fetch/$s_!Mmj3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.png 1272w, https://substackcdn.com/image/fetch/$s_!Mmj3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F21955ffc-e283-4002-8617-77a25fc97fff_1200x675.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 role="img" 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><title></title><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>Replay will be available to Optivem Journal Paid members.</p><h2><strong>Who is this session for</strong></h2><p><strong>Senior Engineers</strong> &amp; <strong>Tech Leads</strong> who want to eliminate manual regression testing, who want to increase quality across their teams<br><br><strong>Who this is NOT for:</strong></p><p>&#8203;If you&#8217;re comfortable accepting constant production issues and manual QA &#8212; this training isn&#8217;t for you.</p><h2><strong>&#127873; Free for Paid Optivem Journal Members</strong></h2><p>If you&#8217;re already a <strong>paid member</strong><br>&#128073; <a href="https://journal.optivem.com/p/discount">Click here to claim your 100% discount</a></p><p>Not a member yet? You can upgrade now and get immediate access to the session (plus replays and future events).<br>&#128073; <a href="https://journal.optivem.com/subscribe">Upgrade to Paid Membership</a></p><h2><strong>If brittle E2E tests and regression bugs are slowing your team down&#8230;</strong></h2><p>You don&#8217;t need more tests.</p><p>You need tests that express behavior and survive change.</p><p>That&#8217;s what we&#8217;ll build in this live training. </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>See you there!</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;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://optivem.thinkific.com/order?ct=4816abff-c281-4105-a6ab-0bceca765f6b"><span>Reserve Your Spot</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Clean Architecture: Use Cases Are NOT Services]]></title><description><![CDATA[Code Example]]></description><link>https://journal.optivem.com/p/clean-architecture-use-cases-are-not-services</link><guid isPermaLink="false">https://journal.optivem.com/p/clean-architecture-use-cases-are-not-services</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 22 Jan 2026 07:02:09 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/449d2cd4-6960-4674-b109-3f4dfcea1290_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>Most &#8220;Clean Architecture&#8221; codebases still revolve around <code>SomethingService</code>.</p><ul><li><p><code>UserService</code></p></li><li><p><code>OrderService</code></p></li><li><p><code>PaymentService</code></p></li></ul><p>And inside those services?</p><ul><li><p>business rules</p></li><li><p>orchestration</p></li><li><p>validation</p></li><li><p>database calls</p></li><li><p>sometimes HTTP logic too</p></li></ul><p>Everything&#8230; everywhere&#8230; with multiple use cases all in one file.</p><h2>Why &#8220;Services&#8221; Are a Dead End</h2><p>Services usually start like this:</p><blockquote><p>&#8220;We&#8217;ll put business logic in services so controllers stay thin.&#8221;</p></blockquote><p>And for a while, it works.</p><p>But over time:</p><ul><li><p>Services grow without a clear responsibility</p></li><li><p>Methods multiply</p></li><li><p>Everything depends on everything</p></li><li><p>Tests become slow, fragile, and heavily mocked</p></li></ul><p>You don&#8217;t know <em>what</em> the system actually does.</p><p>Only <em>what data it has</em>.</p><h2>What a Use Case Actually Is</h2><p>A <strong>use case</strong> represents <strong>one business functionality</strong>.</p><p>Not a resource.<br>Not an entity.<br>Not a technical operation.</p><p>Examples:</p><ul><li><p><code>RegisterUser</code></p></li><li><p><code>PlaceOrder</code></p></li><li><p><code>ApproveLoan</code></p></li><li><p><code>CancelSubscription</code></p></li><li><p><code>GenerateInvoice</code></p></li></ul><p>Each use case answers one question:</p><blockquote><p><em>What does the business want to achieve in this scenario?</em></p></blockquote><h2>Services vs Use Cases</h2><p>Services (group data):</p><ul><li><p>Organized around entities</p></li><li><p>Grow horizontally</p></li></ul><p>Use Cases (express behavior)</p><ul><li><p>Organized around behavior</p></li><li><p>Stay focused</p></li></ul><p>If you have a <code>UserService</code>, ask yourself:</p><blockquote><p>What is the <em>use case</em> here?</p></blockquote><p>Because <strong>&#8220;user&#8221; is not a behavior</strong>.</p><h1>&#128161;Code Example</h1>
      <p>
          <a href="https://journal.optivem.com/p/clean-architecture-use-cases-are-not-services">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[CI/CD Isn’t About Speed. It’s About Safety.]]></title><description><![CDATA[Teams chase faster pipelines and end up with fragile systems.]]></description><link>https://journal.optivem.com/p/cicd-isnt-about-speed-its-about-safety</link><guid isPermaLink="false">https://journal.optivem.com/p/cicd-isnt-about-speed-its-about-safety</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Thu, 15 Jan 2026 07:00:49 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/19a1f97f-cd78-4eb7-be1a-787a7eb119a4_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>CI/CD is usually sold as a promise of speed.</p><ul><li><p>&#8220;Deploy faster.&#8221;</p></li><li><p>&#8220;Release more often.&#8221;</p></li><li><p>&#8220;Ship ten times a day.&#8221;</p></li></ul><p>And yet, most teams I work with deploy <strong>more frequently</strong> than ever &#8212; and still dread it.</p><p>Deployments are green, pipelines are automated, dashboards look healthy, but&#8230;</p><ul><li><p>Slack explodes after releases.</p></li><li><p>People hover over rollback buttons.</p></li><li><p>The customers are complaining.</p></li></ul><p>That&#8217;s the paradox.</p><p>CI/CD didn&#8217;t make teams feel safer&#8230;</p><p>It made them move faster <strong>while staying afraid</strong>.</p><p>The truth is - the team thought that they were practicing CI/CD, but they actually weren&#8217;t. They had a pipeline (they thought that means CI/CD), but didn&#8217;t have adequate automated tests, instead relying on Manual QA.</p><p>If they we truly practicing CI/CD, they would have both safety &amp; speed.</p><h2>The Lie We Were Sold</h2><p>Speed is easy to sell.</p><p>Speed has metrics.<br>Speed has charts.<br>Speed looks good in talks and blog posts.</p><p>So CI/CD got framed as a productivity tool:</p><blockquote><p>&#8220;Look how often we deploy.&#8221;</p></blockquote><p>But deploying often is not the goal.</p><p>Deploying <strong>without fear</strong> is.</p><p>Speed without safety just means you&#8217;re reaching failure sooner.</p><h2>What CI/CD Is <em>Actually</em> Optimizing For</h2><p>At its core, CI/CD optimizes for <strong>risk reduction</strong>.</p><p>Every practice that matters in CI/CD points to the same outcome:</p><ul><li><p>smaller failures</p></li><li><p>earlier feedback</p></li><li><p>faster recovery</p></li><li><p>less human panic</p></li></ul><p>CI/CD is a <strong>risk-management system disguised as automation</strong>.</p><p>When it works, deployments stop being events.<br>They become background noise.</p><p>Not because nothing can go wrong &#8212;<br>but because when something does, you already know <em>how</em> it will go wrong, <em>where</em>, and <em>how to undo it</em>.</p><h2>The Four Safety Nets of Real CI/CD</h2>
      <p>
          <a href="https://journal.optivem.com/p/cicd-isnt-about-speed-its-about-safety">
              Read more
          </a>
      </p>
   ]]></content:encoded></item><item><title><![CDATA[Code Coverage ≠ Quality]]></title><description><![CDATA[Why ATDD Beats Code Coverage Metrics]]></description><link>https://journal.optivem.com/p/code-coverage-doesnt-equal-quality</link><guid isPermaLink="false">https://journal.optivem.com/p/code-coverage-doesnt-equal-quality</guid><dc:creator><![CDATA[Valentina Jemuović]]></dc:creator><pubDate>Tue, 13 Jan 2026 07:02:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/0cf4a630-654d-4849-8fc3-da951cbb1b56_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>Hitting 100% code coverage doesn&#8217;t mean your code works. Here&#8217;s why ATDD matters.</p><p>I remember when my client&#8217;s team first got obsessed with code coverage.</p><p>&#8220;Let&#8217;s hit 90% coverage this sprint!&#8221; the manager said.</p><p>Great, right? Except&#8230; two months later, they had 95% coverage and yet the system kept breaking in production.</p><p>I learned something hard that day: <strong>coverage is just a number</strong>.</p><p>It doesn&#8217;t tell you if your code works, if your business rules are correct, or if anything is maintainable.</p><p>You can execute every line of code and still ship disasters.</p><h2>Knight Capital Group &#8211; $440&#8239;million loss in 45 minutes</h2><p>Here&#8217;s one incident that still keeps me up at night.</p><p>In 2012, Knight Capital Group deployed an update to their trading system.</p><p>An old, previously disabled piece of logic got re&#8209;activated by accident.</p><p>The system <strong>started sending orders it shouldn&#8217;t have.</strong></p><p>Within <strong>45 minutes</strong>, they lost <strong>$440 million</strong>. Just like that.</p><p>Here&#8217;s the kicker:</p><p>No amount of <em>code coverage</em>&#8212;90%, 95%, or 100%&#8212;would have prevented the disaster.</p><p>Why? Because this wasn&#8217;t a line&#8209;not&#8209;executed problem. Only <strong>Acceptance Tests</strong> could have caught this before it hit production.</p><p>Metrics can make you <em>feel safe</em>. Tests that focus on behavior actually <em>keep you safe</em>.</p><h2>Code Coverage is a Trap</h2><p>Code coverage only tells you code ran.</p><p>Not that it worked.</p><p><strong>Running code &#8800; verifying behavior</strong>.</p><p>I learned: chasing metrics doesn&#8217;t make you a better developer&#8212;thinking in terms of behavior does.</p><h2>Metrics &#8800; Safety</h2><p>Next time someone asks about coverage, ask: &#8220;Does it actually tell me my system works?&#8221;</p><blockquote><p>If the answer is no, stop worrying about the percentage and start thinking about behavior.</p></blockquote><div><hr></div><p>This is exactly why I rely on Acceptance Tests. Unit Tests with 100% code coverage are not enough.</p><p>They protect you from the stuff metrics can&#8217;t see&#8212;accidental behavior changes, re-activated logic, &#8220;how did this even happen?&#8221; bugs.</p><p>If you want to learn how to write acceptance tests that make production failures boring instead of catastrophic&#8230;</p><p>&#128640; Join me for <strong>Acceptance Testing (Live Training) </strong>on Wed 25th Feb (17:00 - 19:00 CET). </p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://optivem.thinkific.com/products/live_events/2026-02-25-acceptance-testing-live-training&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/2026-02-25-acceptance-testing-live-training"><span>&#127942; Register now</span></a></p><p><em>P.S. Regular price: $97 (free access for Optivem Journal paid subscribers - see event description for details)</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>]]></content:encoded></item></channel></rss>