<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: gusdmoreira</title>
    <description>The latest articles on DEV Community by gusdmoreira (@gusdmoreira).</description>
    <link>https://dev.to/gusdmoreira</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F322817%2F8fccfe66-b150-4b5b-b148-db1f966d997d.jpg</url>
      <title>DEV Community: gusdmoreira</title>
      <link>https://dev.to/gusdmoreira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gusdmoreira"/>
    <language>en</language>
    <item>
      <title>Beyond the Green Test: A Pragmatic Framework for Evaluating AI Code Quality</title>
      <dc:creator>gusdmoreira</dc:creator>
      <pubDate>Tue, 06 Oct 2026 15:34:15 +0000</pubDate>
      <link>https://dev.to/gusdmoreira/beyond-the-green-test-a-pragmatic-framework-for-evaluating-ai-code-quality-4o1c</link>
      <guid>https://dev.to/gusdmoreira/beyond-the-green-test-a-pragmatic-framework-for-evaluating-ai-code-quality-4o1c</guid>
      <description>&lt;p&gt;You prompt your favorite AI coding assistant: &lt;em&gt;"Write a service that processes bulk user telemetry, validates incoming records, and handles batch publishing."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Within seconds, pristine code streams across your screen. The structure looks clean, the syntax leverages modern language features, and it even includes neat documentation comments. You drop it into your repository, run your test suite, and watch the console turn bright green.&lt;/p&gt;

&lt;p&gt;Ship it, right?&lt;/p&gt;

&lt;p&gt;Not so fast.&lt;/p&gt;

&lt;p&gt;While tools like GitHub Copilot, Claude, and specialized coding agents are incredible velocity boosters, they operate on probability, not deep contextual understanding. They excel at the &lt;em&gt;happy path&lt;/em&gt; but frequently gloss over the messy realities of production systems: concurrency nuances, resource management under heavy load, security implications, and subtle domain edge cases.&lt;/p&gt;

&lt;p&gt;If you blindly merge AI-generated code without a systematic evaluation framework, you are essentially outsourcing your technical debt to a non-deterministic generator.&lt;/p&gt;

&lt;p&gt;Let's look at a practical, robust approach to auditing and validating AI-generated code, using Java for our concrete examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Shift-Left Validation: Define the Contract via TDD
&lt;/h2&gt;

&lt;p&gt;The biggest trap developers fall into is letting the AI write both the implementation &lt;em&gt;and&lt;/em&gt; the expectations. If the model hallucinates a requirement or misunderstands a business rule, its generated unit tests will happily validate its own flawed logic.&lt;/p&gt;

&lt;p&gt;Instead, practice AI-Driven TDD: &lt;strong&gt;Write the test contract yourself before invoking the assistant.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you need a thread-safe rate limiter service. Before asking the AI to write the class, write your strict unit test suite covering concurrent threads, edge limits, and time windows. For instance, in Java using JUnit 5 and AssertJ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.ratelimiter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.junit.jupiter.api.DisplayName&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.time.Duration&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.CountDownLatch&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.ExecutorService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.Executors&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.concurrent.atomic.AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertj&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;core&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Assertions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RateLimiterServiceTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="nd"&gt;@DisplayName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should allow requests within limit and throttle excess under concurrency"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;testConcurrentRateLimiting&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;RateLimiterService&lt;/span&gt; &lt;span class="n"&gt;limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RateLimiterService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Duration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofSeconds&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
        &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;totalThreads&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;ExecutorService&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalThreads&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt; &lt;span class="n"&gt;latch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CountDownLatch&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;totalThreads&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt; &lt;span class="n"&gt;allowedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AtomicInteger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;totalThreads&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limiter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tryAcquire&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"user-123"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;allowedCount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;incrementAndGet&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;latch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;countDown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;});&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;latch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;await&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shutdown&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Exactly 5 should pass out of 20 concurrent attempts&lt;/span&gt;
        &lt;span class="n"&gt;assertThat&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowedCount&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;isEqualTo&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Danger of the "Fake Contract" (Make Your Test Bleed First)&lt;br&gt;
Writing the contract yourself prevents the AI from validating its own flaws, but it opens the door to a quieter issue: you can write a test that passes for the wrong reasons.&lt;/p&gt;

&lt;p&gt;For example, if you ask an AI for a deduplication function and write an assertion checking only that the output is a subset of the original elements, that test will happily pass even if the model returns the list completely untouched with duplicates intact. A test that has never failed is not a guard—it is a comment that runs.&lt;/p&gt;

&lt;p&gt;The Golden Rule: Before you trust your test suite, break the implementation on purpose (return a dummy value or an empty list) and watch it go red. Pre-fix red is the only real evidence that a test is actually capable of failing. If a known-bad build still passes your test, your contract is an illusion.&lt;/p&gt;

&lt;p&gt;By establishing this rigorous contract upfront, you give the AI a rigid mathematical boundary. When it returns the implementation, your test suite acts as an objective security guard.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Curate a Domain-Specific "Golden Dataset"
&lt;/h2&gt;

&lt;p&gt;For complex data transformations, parsers, or validation rules, unit tests aren't always enough to catch structural discrepancies. This is where you build a &lt;strong&gt;Golden Dataset&lt;/strong&gt;, a version-controlled reference collection containing edge cases specific to your domain.&lt;/p&gt;

&lt;p&gt;You can maintain this as a resource file (like JSON) inside your project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"inputUserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USR-001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;tier&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;PREMIUM&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;amount&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 1500.00}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expectedStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ACCEPTED"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"inputUserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USR-002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MALFORMED_JSON_STRING"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expectedStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REJECTED_MALFORMED"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"inputUserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USR-999"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;tier&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;EXPIRED&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;amount&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 50.00}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expectedStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REJECTED_UNAUTHORIZED"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"inputUserId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;tier&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;BASIC&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;amount&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: 10.00}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"expectedStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"REJECTED_NULL_ID"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your automated evaluation test reads this dataset, passes every payload through the AI-generated service, and asserts the outcomes. When you refactor prompts or upgrade your model version, running this dataset guarantees zero regressions.&lt;/p&gt;

&lt;p&gt;Watch Out for Rejection Bias: When building your golden dataset, it is easy to overload it with edge cases that must be rejected (nulls, empty strings, malformed syntax). If your dataset is heavily skewed toward rejections, you risk driving the AI validator toward the worst possible convergence: a service that rejects everything. A system that says "no" to every input has zero false positives on rejections, but it is completely useless. Ensure that the cases which must be accepted carry just as much weight and diversity as the ones that fail.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Beyond Correctness: The Production Audit Checklist
&lt;/h2&gt;

&lt;p&gt;Once the code passes your functional tests, you must conduct a targeted code review focusing on dimensions that automated tests often miss:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resource Management &amp;amp; Memory:&lt;/strong&gt; Did the model properly handle connection closures, stream disposal, or memory lifecycle hooks? Look out for unclosed handles or collections accumulating state indefinitely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency &amp;amp; Thread Safety:&lt;/strong&gt; LLMs frequently default to non-thread-safe data structures in multi-threaded contexts. Ensure safe alternatives or proper synchronization mechanisms are in place.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exception Hygiene:&lt;/strong&gt; Does the code swallow broad exceptions or suppress stack traces? Verify that errors throw meaningful, domain-specific exceptions with appropriate logging context.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. Treat Prompts as Architecture, Not Chat History
&lt;/h2&gt;

&lt;p&gt;If your team relies heavily on AI workflows, automated prompt generation, or contextual system instructions, stop treating prompts like casual chat messages.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Version Control:&lt;/strong&gt; Store your prompt templates in dedicated version-controlled directories rather than hardcoding them inline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency Metrics:&lt;/strong&gt; Measure prompt reliability across multiple iterations before deploying changes that affect core business pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The mark of a senior engineer in the age of AI isn't how fast they can copy-paste generated code; it's how rigorously they can evaluate it.&lt;/p&gt;

&lt;p&gt;Treat AI-generated code with the exact same professional skepticism you would apply to code submitted by an external contractor. Write your tests first, audit for real-world production constraints, and remember: &lt;strong&gt;The best developers using AI are not the ones who trust it blindly, but the ones who verify it with absolute precision.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>Use SOLID Principles To Become Better Automation Tester</title>
      <dc:creator>gusdmoreira</dc:creator>
      <pubDate>Thu, 24 Jun 2021 20:51:47 +0000</pubDate>
      <link>https://dev.to/gusdmoreira/use-solid-principles-to-become-better-automation-tester-3kg9</link>
      <guid>https://dev.to/gusdmoreira/use-solid-principles-to-become-better-automation-tester-3kg9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Are you using most recommended and fundamental SOLID principles in your automation?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You Need To Use SOLID Principles?
&lt;/h2&gt;

&lt;p&gt;Like any development work, automation also needs to be planned, designed, developed, maintained and extended over time. Your automation code or automation framework is an application which is testing your actual application. That’s why SOLID principles are highly recommend for your automation to make it easy to understand, use, maintain and extend over time. When SOLID principles are not applied, your automation might become fragile, hard to maintain and hard to extend very soon.&lt;/p&gt;

&lt;p&gt;Even if you are using different programming language like C#, Python or automation tool like HP UFT/QTP for automation, you should definitely consider applying SOLID principles to make your automation tester life easy. Please note that we will be discussing about applying SOLID principles to web automation using Selenium &amp;amp; Java. But, once you learn the concepts, you can easily apply them to any object-oriented development.&lt;/p&gt;

&lt;p&gt;In this post, we will be exploring what SOLID principles stands for. We will be having one post for each principle with real life examples from web automation using Selenium &amp;amp; Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are The SOLID Principles?
&lt;/h2&gt;

&lt;p&gt;SOLID principles are the first five object-oriented design principles by Robert C. Martin, popularly known as Uncle Bob.&lt;/p&gt;

&lt;p&gt;SOLID is an acronym where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;S stands for Single Responsibility Principle (SRP)&lt;/li&gt;
&lt;li&gt;O stands for Open Closed Principle (OCP)&lt;/li&gt;
&lt;li&gt;L stands for Liskov Substitution Principle (LSP)&lt;/li&gt;
&lt;li&gt;I stands for Interface Segregation Principle (ISP)&lt;/li&gt;
&lt;li&gt;D stands for Dependency Inversion Principle (DIP)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s explore and understand each individual principle. Also, let’s find out how these SOLID principles make you better automation tester.&lt;/p&gt;

&lt;h2&gt;
  
  
  Single Responsibility Principle (SRP): A class should have one, and only one, reason to change
&lt;/h2&gt;

&lt;p&gt;In other words to achieve this, a class should only have a single responsibility and it should do that very well. Every class in your automation should only have a single responsibility and that all of its methods should be aligned with that responsibility.&lt;/p&gt;

&lt;p&gt;Let’s try to understand this principle by looking at an example from our daily life. When you are driving a car/bike, you want to fully concentrate on the single responsibility – driving. You don’t want to do or concentrate on other tasks like talking on a phone, eating.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automation Examples:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Page Object Framework implements SRP very well. We are going to have one class responsible for only one web page in the application. We shouldn’t have a very big class with many responsibilities like test methods, UI action methods, excel read/write methods.&lt;/li&gt;
&lt;li&gt;We can have very specific helper classes like ExcelHelper, DatabaseHelper to work with a excel file or database to implement Data Driven Framework.&lt;/li&gt;
&lt;li&gt;Selenium API has browser specific driver classes like FirefoxDriver, ChromeDriver, InternetExplorerDriver. FirefoxDriver has single responsibility to drive the Firefox browser and it drives the Firefox browser very well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We should also have methods in classes that are very specific like &lt;code&gt;loginAs(username, password)&lt;/code&gt; to login with given username and password, &lt;code&gt;testSuccessfulLogin(username,password)&lt;/code&gt; which tests only one thing that login should be successful with valid username/password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Open Closed Principle (OCP): Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification
&lt;/h2&gt;

&lt;p&gt;You should be able to easily add additional functionality for a class without changing its code. OCP says that a class should be open for extension and closed for modification. The “closed” part of the rule states that once a class has been developed and tested, the class code shouldn’t change except for any bug fixes. The “open” part of the rule states that you should be able to extend existing code in order to introduce new functionality. We are trying to add new functionality without modifying the existing code/functionality and by adding new classes/code as required. This is very important to minimise the impact of changes and errors from existing code.&lt;/p&gt;

&lt;p&gt;Let’s try to understand this principle by looking at an example from our daily life. Let’s say you live in a 2 bedroom house and you are looking for a 3 bedroom house due to reasons like growing children. If you have free/unused space available, it’s very easy to extend the house by building another bedroom. Also with this, you are minimise the impact of changes on the existing 2 bedroom house.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automation Example:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s say that you are automating an online store application. You have Customer class to represent store customers and respective related customer actions. Now, your company has introduced VIP customer concept to reward loyal customers with discounts and free delivery. To implement VIP customer behaviour in your automation, OCP suggests that keep the Customer class same without modifying it and create a new VipCustomer class by inheriting from Customer class. Now in the VipCustomer class extend the behaviour as required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Liskov Substitution Principle (LSP): Subtypes must be substitutable for their base types
&lt;/h2&gt;

&lt;p&gt;When you pass subtype for a base type argument or when you assign/instantiate base type with subtype, the program/code should work properly without changing its behaviour and shouldn’t break. This principle was introduced by and named after Barbara Liskov.&lt;/p&gt;

&lt;p&gt;Let’s try to understand this principle by looking at an example from our daily life. Let’s say that you have a wall clock at home or you have a wrist watch. They both need batteries to work. If you buy batteries as per the specifications from any brand like Panasonic, Sony, Duracell, you expect the wall clock or wrist watch to work properly without any issues when powered by those batteries.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automation Example:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Considering the above example of automating an online store application with Customer and VIP Customer categories, let’s say there is a method &lt;code&gt;calculateDeliveryCharge(Customer customer, OrderInfo orderInfo)&lt;/code&gt; which calculates delivery charge when customer and order information is passed. When we pass &lt;code&gt;Customer&lt;/code&gt; object to &lt;code&gt;calculateDeliveryCharge()&lt;/code&gt; method with order information, it should return delivery charge. Note that we are providing free delivery to VIP Customers. So, when we pass &lt;code&gt;VipCustomer&lt;/code&gt; object for customer argument to &lt;code&gt;calculateDeliveryCharge()&lt;/code&gt; method with order information, the program/code should work properly without changing its behaviour and shouldn’t throw any exceptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interface Segregation Principle (ISP): Make fine grained interfaces that are client specific
&lt;/h2&gt;

&lt;p&gt;It’s good to have small role specific interfaces rather than one big general interface. ISP states that clients should never be forced to implement interfaces that they don’t use or clients should never be forced to depend on methods that they don’t use. When a class depends upon another class, the number of members visible from the another class to the dependent class should be minimised. When you apply the ISP, classes implement multiple smaller role specific interfaces and dependent classes depend on required role specific interfaces for the given task.&lt;/p&gt;

&lt;p&gt;Let’s try to understand this principle by looking at an example from our daily life. When you are travelling in a train and when ticket inspector wants to check your ticket, you will be showing only your ticket and not all your luggage. Similarly, ticket inspector wants to check your ticket only and not any other belongings of you. We should reveal/expose only what’s required for the given task.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automation Example:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s again take the example of automating an online store application. You have to test that an email is received by the customer after successful purchase. Let’s say that you have an EmailHelper class with a method &lt;code&gt;isEmailReceived()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Your Customer class might have so many fields and methods like &lt;code&gt;firstName, lastName, emailAddress, phoneNumber, deliveryAddress, billingAddress, getPurchaseHisotry(), getOrdersInProgress()&lt;/code&gt;. But &lt;code&gt;isEmailReceived()&lt;/code&gt; method only needs to know &lt;code&gt;firstName, lastName, emailAddress&lt;/code&gt; fields and doesn’t need to now all the other fields/methods of Customer class.&lt;/p&gt;

&lt;p&gt;Consider implementation like &lt;code&gt;isEmailReceived(Customer customer, string subject, string body)&lt;/code&gt;: We are exposing whole Customer class in &lt;code&gt;isEmailReceived()&lt;/code&gt; method and then one can easily access/change details like &lt;code&gt;phoneNumber, deliveryAddress, billingAddress&lt;/code&gt; in &lt;code&gt;isEmailReceived()&lt;/code&gt; method. We want to minimize the risk to Customer object by passing only details that are required to the clients/methods. We can achieve this by below implementation by using an &lt;code&gt;Emailable&lt;/code&gt; interface which represents a contact that can be emailed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;interface Emailable {
    String getFirstName();
    String getLastName();
    String getEmailAddress();
}

class Customer implements Emailable {
    String firstName, lastName, emailAdddress;
    String phoneNumber, deliveryAddress, billingAddress;

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public String getEmailAddress() {
        return emailAdddress;
    }
}

class EmailHelper {
    public boolean isEmailReceived(Emailable recipient, String subject, String body) {
        // Logic to check whether recipient recieved email or not

        // You can use recipient.getFirstName(), recipient.getLastName(), recipient.getEmailAddress() to get recipient details

        // When you pass Customer object to this method, only Emailable interface methos are available here and no other details are exposed from Customer Object

        // Customer object is safe from any changes

        return false;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we can further reuse Emailable interface for other types like StoreManager, Supplier to whom your online store sends emails. With this approach, we can use &lt;code&gt;isEmailReceived()&lt;/code&gt; method with any implementation that implements Emailable interface and respective input object for recipient argument is safe from any changes/actions. I hope now you have understood the power of ISP.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Other Automation Example:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Selenium API has good examples of ISP. Selenium API has a number of very fine grained, role based client specific interfaces like WebDriver, WebElement, Alert. We should favour role based interfaces instead of generic interfaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Inversion Principle (DIP): Depend on abstractions, not on concretions
&lt;/h2&gt;

&lt;p&gt;DIP states that Software entities (classes, modules, functions, etc.) should depend on abstractions (like interfaces) and not on concretions (like concrete class types). DIP promotes code to an interface approach. For example, in Selenium automation code, we code to a WebDriver interface variable “driver” whenever we want to work with web browser and the same code works for any browser type like FirefoxDriver, ChromeDriver, InternetExploerDriver which implements the WebDriver interface.&lt;/p&gt;

&lt;p&gt;DIP mainly suggests below 2 rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;High level modules should not depend upon low level modules. Both should depend upon abstractions.&lt;/li&gt;
&lt;li&gt;Abstractions should not depend upon details. Details should depend upon abstractions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s try to understand this principle by looking at an example from our daily life. When you go to a cash machine/ATM, the cash machine/ATM expects a valid debit/credit card. The machine has a dependency on valid card abstraction and not on specific concrete type cards like only Visa, only Maestro or only issued by specific bank. The machine works for any valid card type implementation and we are providing the card to the machine from outside which provides so much flexibility and easiness to use the machine.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Automation Example:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look at below page object class for a Login Page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class LoginPage {
    private WebDriver driver;

    public LoginPage(WebDriver driver) {
        this.driver = driver;
    }

    public void loginAs(String username, String password) {
        driver.findElement(By.id("username")).sendKeys(username);
        driver.findElement(By.id("password")).sendKeys(password);
        driver.findElement(By.name("login")).click();
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can observe following with respect to DIP from above LoginPage class:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is depending on the abstraction and all the code is written against WebDriver interface so that the code can work with any implementation like FirefoxDriver, ChromeDriver, InternetExploerDriver&lt;/li&gt;
&lt;li&gt;The implementation for the WebDriver interface should be passed by the client through constructor LoginPage(WebDriver driver) when creating the LoginPage object.&lt;/li&gt;
&lt;li&gt;So the code works for any implementation and we can pass the implementation when creating the object. This gives us lot of flexibility and it will be easy to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Ready To Use/Apply SOLID Principles In Your Automation…
&lt;/h2&gt;

&lt;p&gt;I hope you have got good understanding of SOLID principles by now. If it seems complicated, don’t worry and things will be very clear in the next posts. Let’s look at each principle individually in the next posts to find out how these principles makes us better automation testers and makes our automation easy to understand, use, maintain and extend.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>solid</category>
      <category>selenium</category>
    </item>
  </channel>
</rss>
