<?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: Samuel Ko</title>
    <description>The latest articles on DEV Community by Samuel Ko (@samuelko123).</description>
    <link>https://dev.to/samuelko123</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2667586%2Fc864facf-263a-4e1f-93bc-baa9c75df737.jpeg</url>
      <title>DEV Community: Samuel Ko</title>
      <link>https://dev.to/samuelko123</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samuelko123"/>
    <language>en</language>
    <item>
      <title>Idea Validation: ProofMate</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Sun, 13 Apr 2025 07:58:51 +0000</pubDate>
      <link>https://dev.to/samuelko123/idea-validation-proofmate-5685</link>
      <guid>https://dev.to/samuelko123/idea-validation-proofmate-5685</guid>
      <description>&lt;p&gt;Hi everyone!&lt;/p&gt;

&lt;p&gt;I’ve been sitting on an idea and I’d love to gather early feedback from you.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;As a software engineer, the annual performance review is one of those tasks that always ends up draining more time and energy than it should.&lt;/p&gt;

&lt;p&gt;Personal experience:&lt;/p&gt;

&lt;p&gt;Year 1 - I dug through Teams messages, Pull Requests, Jira tickets, and more, trying to piece together what I’d done.&lt;/p&gt;

&lt;p&gt;Year 2 - I started recording my "achievements" (with links) in Confluence throughout the year, but still had to look for extra details when writing my summary.&lt;/p&gt;

&lt;p&gt;Year 3 - Same as Year 2, plus ChatGPT polishing before pasting it into Workday.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Idea
&lt;/h3&gt;

&lt;p&gt;I have this idea. Let's call it "ProofMate".&lt;/p&gt;

&lt;p&gt;It will aggregate data from various platform for us, and generate a summary using AI.&lt;/p&gt;

&lt;p&gt;
  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml
actor User as Engineer

package "ProofMate System" {
    rectangle "Data Collector" as Collector
    rectangle "AI Assistant" as AI
    rectangle "Self-Review Draft" as Draft
}

cloud "GitHub" as GitHub
cloud "Jira" as Jira
cloud "Confluence" as Confluence
cloud "Microsoft Teams" as Teams
cloud "Slack" as Slack

Engineer --&amp;gt; Collector : Authenticate &amp;amp; Connect Accounts
Collector --&amp;gt; GitHub : Fetch commits &amp;amp; PRs
Collector --&amp;gt; Jira : Fetch tickets &amp;amp; issues
Collector --&amp;gt; Confluence : Fetch documents
Collector --&amp;gt; Teams : Fetch chat summaries
Collector --&amp;gt; Slack : Fetch conversations

Collector --&amp;gt; AI : Send structured activity data
AI --&amp;gt; Draft : Generate draft review
Engineer --&amp;gt; Draft : Review &amp;amp; Edit

@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fooib73qyy21abcthu9vw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fooib73qyy21abcthu9vw.png" alt="Conceptual diagram"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Considerations
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Privacy
&lt;/h4&gt;

&lt;p&gt;Work data is often sensitive. Feeding raw material to AI models risks exposure. Should this be an offline-first app like jwt.io? More research is needed.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Data Retention
&lt;/h4&gt;

&lt;p&gt;The app could be designed to store only the final summary (perhaps even save directly to your Google Drive). No long-term logging of your raw work artifacts.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Security vs. User Friendliness
&lt;/h4&gt;

&lt;p&gt;Should API tokens be destroyed after each session?&lt;br&gt;
How can re-authentication be made as smooth as possible?&lt;/p&gt;

&lt;h3&gt;
  
  
  Your thoughts
&lt;/h3&gt;

&lt;p&gt;I’d love to know:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does your company ask for self-reviews?&lt;/li&gt;
&lt;li&gt;How do you usually prep for them?&lt;/li&gt;
&lt;li&gt;Would you trust an AI to generate a draft based on your real work data?&lt;/li&gt;
&lt;li&gt;What integrations would be a must-have for you?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’d really appreciate your feedback! 🙏&lt;/p&gt;

&lt;p&gt;(P. S. if you're also passionate about this idea, let's connect and build together!)&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>buildinpublic</category>
      <category>idea</category>
      <category>saas</category>
    </item>
    <item>
      <title>Secure Data Handling - Part 1 - Understanding Encoding</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Wed, 09 Apr 2025 13:19:24 +0000</pubDate>
      <link>https://dev.to/samuelko123/secure-data-handling-part-1-understanding-encoding-5f95</link>
      <guid>https://dev.to/samuelko123/secure-data-handling-part-1-understanding-encoding-5f95</guid>
      <description>&lt;p&gt;Have you ever entered a search term into a web browser, hit "Enter", and noticed that the URL changes to something like this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://example.com/search?query=hello%20world%21
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;%20&lt;/code&gt; and &lt;code&gt;%21&lt;/code&gt; represent a space (&lt;code&gt;&lt;/code&gt;) and an exclamation mark (&lt;code&gt;!&lt;/code&gt;) respectively. They are part of &lt;a href="https://en.wikipedia.org/wiki/Percent-encoding" rel="noopener noreferrer"&gt;URL encoding&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Encoding?
&lt;/h2&gt;

&lt;p&gt;Encoding is the process of converting information into a different format, typically for storage or transmission.&lt;/p&gt;

&lt;p&gt;A simple example is the "A1Z26" cipher, where each letter of the alphabet is replaced by its corresponding number (A=1, B=2, C=3, etc.). For instance, the word &lt;code&gt;apple&lt;/code&gt; would be encoded as &lt;code&gt;1-16-16-12-5&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Applications of Encoding
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Base64 Encoding
&lt;/h3&gt;

&lt;p&gt;Base64 encoding is one of the most common encoding schemes used to convert binary data into a text format. It’s often used to encode binary files (like images or attachments) so they can be transmitted over protocols that only support text (like HTTP, JSON, or email).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
Plain Text: &lt;code&gt;Hello, world!&lt;/code&gt;&lt;br&gt;
Base64 Encoded: &lt;code&gt;SGVsbG8sIHdvcmxkIQ==&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encoding files for email attachments&lt;/li&gt;
&lt;li&gt;Embedding images directly into HTML or CSS&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. URL Encoding
&lt;/h3&gt;

&lt;p&gt;URL encoding (also called "percent encoding") converts special characters into a safe format for URLs. For example, spaces and characters like &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;=&lt;/code&gt;, and &lt;code&gt;?&lt;/code&gt; have special meanings in URLs, so they need to be encoded before transmission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
Plain Text: &lt;code&gt;Hello world!&lt;/code&gt;&lt;br&gt;
URL Encoded: &lt;code&gt;Hello%20world%21&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Encoding user input for GET requests&lt;/li&gt;
&lt;li&gt;Encoding query strings or path parameters in URLs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. HTML/XML Encoding
&lt;/h3&gt;

&lt;p&gt;HTML or XML encoding ensures that special characters in data (like &lt;code&gt;&amp;lt;&lt;/code&gt;, &lt;code&gt;&amp;gt;&lt;/code&gt;, or &lt;code&gt;&amp;amp;&lt;/code&gt;) are correctly interpreted by web browsers or other systems reading the data. For example, the character &lt;code&gt;&amp;lt;&lt;/code&gt; in HTML needs to be encoded as &lt;code&gt;&amp;amp;lt;&lt;/code&gt; to avoid being mistaken for HTML tags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;br&gt;
Plain Text: &lt;code&gt;x &amp;lt; 1&lt;/code&gt;&lt;br&gt;
HTML Encoded: &lt;code&gt;x&amp;amp;nbsp;&amp;amp;lt;&amp;amp;nbsp;1&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This encoding is essential for displaying content within web pages without unexpected behavior.&lt;/p&gt;

&lt;p&gt;Use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Escaping special characters in HTML or XML&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Encoding is Not Security
&lt;/h2&gt;

&lt;p&gt;It's important to note that &lt;strong&gt;encoding is not a security measure&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Encoding is &lt;strong&gt;reversible&lt;/strong&gt;. When you encode a piece of data, anyone can decode it. For example, with Base64 encoding, the padding (&lt;code&gt;==&lt;/code&gt;) clearly signals the encoding method. Even if you invent a new encoding scheme, malicious actors can often identify patterns and reverse the process.&lt;/p&gt;

&lt;p&gt;When working with sensitive information like passwords or personal details, you'll need encryption or hashing, which are designed for data protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Encoding&lt;/strong&gt; is a simple yet essential concept in modern computing. It ensures that data can be safely and correctly transmitted across systems that might have different ways of processing it.&lt;/p&gt;

&lt;p&gt;In the next part of this series, we’ll dive into &lt;strong&gt;encryption&lt;/strong&gt;, which goes a step further to protect your data during transmission. Stay tuned!&lt;/p&gt;

</description>
      <category>security</category>
      <category>programming</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Health Check for MySQL in a Docker Container</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Thu, 03 Apr 2025 12:27:47 +0000</pubDate>
      <link>https://dev.to/samuelko123/health-check-for-mysql-in-a-docker-container-3m5a</link>
      <guid>https://dev.to/samuelko123/health-check-for-mysql-in-a-docker-container-3m5a</guid>
      <description>&lt;h3&gt;
  
  
  Glossary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Docker_(software)" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/MySQL" rel="noopener noreferrer"&gt;MySQL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Background
&lt;/h3&gt;

&lt;p&gt;When launching a full-stack web application, whether locally or in production, sequencing the dependencies is critical. For example, if we start serving web pages before the back-end services are ready, users may encounter broken links, 500 errors, or timeout issues that can be hard to debug.&lt;/p&gt;

&lt;p&gt;Docker provides a built-in mechanism called &lt;code&gt;HEALTHCHECK&lt;/code&gt;, which allows us to ensure that the database is ready to accept connections before exposing the REST API endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Not-So-Perfect Way
&lt;/h3&gt;

&lt;p&gt;One obvious way to health-check a MySQL database is to use &lt;code&gt;mysqladmin ping&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;mydb&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysql&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_ROOT_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;root&lt;/span&gt;
      &lt;span class="na"&gt;MYSQL_ROOT_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;password&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mysqladmin ping&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, according to the &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/mysqladmin.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt;, &lt;code&gt;mysqladmin ping&lt;/code&gt; would:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check whether the server is available. The return status from mysqladmin is 0 if the server is running, 1 if it is not. This is 0 even in case of an error such as Access denied, because this means that the server is running but refused the connection, which is different from the server not running. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It implies that &lt;code&gt;mysqladmin ping&lt;/code&gt; is not a reliable way to check readiness. It could lead to intermittent REST API errors, which can be challenging to detect and diagnose.&lt;/p&gt;

&lt;p&gt;We can validate such behavior by inspecting the Docker container: &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy4k4rcd5gn5lck75e39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdy4k4rcd5gn5lck75e39.png" alt="A screenshot of inspecting a MySQL Docker container" width="800" height="410"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Better Way
&lt;/h3&gt;

&lt;p&gt;Instead of simply pinging the server, a better health check is to send an actual SQL query to ensure the database is ready to handle requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test: mysql --user=root --password=password --execute="SHOW DATABASES;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When inspecting the Docker container, we can observe the transition of the exit code from 1 (unavailable) to 0 (ready), indicating the database is now fully operational:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi5v4ab4heujcukim641.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxi5v4ab4heujcukim641.png" alt="A screenshot of inspecting a MySQL Docker container" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;mysqladmin ping&lt;/code&gt; could return a false positive, indicating the server is running even when it's not ready to serve queries.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;SHOW DATABASES;&lt;/code&gt; to verify the database is ready to accept connections and handle requests.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Have you encountered any issues with MySQL health checks in Docker? Or do you have any tips to share? Drop a comment below and let us know your experiences! We’d love to hear from you.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>mysql</category>
      <category>docker</category>
      <category>learning</category>
    </item>
    <item>
      <title>The Bug That Almost Made It to Production</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Wed, 26 Mar 2025 13:27:10 +0000</pubDate>
      <link>https://dev.to/samuelko123/the-bug-that-almost-made-it-to-production-2fnd</link>
      <guid>https://dev.to/samuelko123/the-bug-that-almost-made-it-to-production-2fnd</guid>
      <description>&lt;p&gt;In the fast-paced world of software engineering, there are two kinds of bugs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bugs that made it to production&lt;/li&gt;
&lt;li&gt;Bugs that didn't&lt;/li&gt;
&lt;li&gt;Bugs that were caused by "off-by-one" error&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Today, I would like to share a story of #2 - one that was caught at the last possible moment.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup: A Simple Clean-up Exercise
&lt;/h3&gt;

&lt;p&gt;At a retail company with an e-commerce website, a junior developer was assigned a straightforward task: deprecate an unused field from an internal REST API.&lt;/p&gt;

&lt;h3&gt;
  
  
  How The Bug Was Born
&lt;/h3&gt;

&lt;p&gt;As the junior dev navigated through 22 files to remove traces of the deprecated field, they spotted what looked like a "refactoring opportunity":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ProductType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BEST_SELLER&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;doSomeThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;doSameThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Noticing that both branches seemed to do the same thing (or so they thought), they “cleaned it up” to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;doSameThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But there was a tiny detail that went unnoticed: a subtle difference in function spelling.&lt;/p&gt;

&lt;p&gt;And because this was legacy code, no unit tests covered this logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code Review That Didn't Catch It
&lt;/h3&gt;

&lt;p&gt;By the time the pull request (PR) was ready, the developer’s IDE had also applied auto-linting across the touched files.&lt;/p&gt;

&lt;p&gt;So, the PR ended up like this:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7feg2bnfs59cq9x2dkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb7feg2bnfs59cq9x2dkh.png" alt="999 lines added, 1575 lines deleted" width="530" height="142"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result was, as expected, "LGTM".&lt;/p&gt;

&lt;h3&gt;
  
  
  The Disabled End-To-End Test
&lt;/h3&gt;

&lt;p&gt;Meanwhile, another team maintained an end-to-end (E2E) test for the &lt;code&gt;BEST_SELLER&lt;/code&gt; product. This test simulated a user placing an order on the website.&lt;/p&gt;

&lt;p&gt;Unfortunately, this test failed intermittently due to data issues, such as the test product going out-of-stock.&lt;/p&gt;

&lt;p&gt;So, when the test failed, the reflex action of the Quality Engineer (QE) was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Disable the test first.&lt;/li&gt;
&lt;li&gt;Investigate and rectify the data issue next.&lt;/li&gt;
&lt;li&gt;Don’t be the person blocking a production release.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hsa0yr2jqn75v0usjd2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1hsa0yr2jqn75v0usjd2.png" alt="If test fails, remove the test" width="640" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This time, however, something was different.&lt;/p&gt;

&lt;p&gt;After disabling the test, the QE verified that the test product was actually in-stock, so they came to me (the software engineer) for help.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Pressure to Release
&lt;/h3&gt;

&lt;p&gt;It was a lucky day - both frontend and backend had production releases scheduled.&lt;/p&gt;

&lt;p&gt;The QE team, having dealt with countless data issues in the past, was inclined to dismiss the issue and sign off on the releases.&lt;/p&gt;

&lt;p&gt;But something felt off. The issue spanned across a range of products within the staging environment, not just the test product.&lt;/p&gt;

&lt;p&gt;With pressure mounting, we escalated the issue to the Delivery Lead, who thankfully was understanding and gave us time to investigate.&lt;/p&gt;

&lt;p&gt;This decision saved us.&lt;/p&gt;

&lt;p&gt;By systematically comparing staging vs production behavior, we finally traced the issue back to the root cause — a tiny refactoring mistake hidden in a big PR.&lt;/p&gt;

&lt;p&gt;Had we not caught it, customers trying to buy our best-selling products wouldn’t have been able to place orders.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Takeaways
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. One PR should do one thing only.
&lt;/h4&gt;

&lt;p&gt;Mixing refactoring with unrelated changes makes it harder to review (and catch bugs).&lt;/p&gt;

&lt;h4&gt;
  
  
  2. When refactoring, ensure the changes are covered by unit tests.
&lt;/h4&gt;

&lt;p&gt;If there aren’t any, write them first.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. When enforcing a linting rule, reformat the whole codebase in the same PR as well.
&lt;/h4&gt;

&lt;p&gt;The next person who raised a PR will thank you.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Never disable an E2E test without fully understanding the root cause.
&lt;/h4&gt;

&lt;p&gt;A failing test might be &lt;em&gt;the only warning sign&lt;/em&gt; before disaster strikes.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>e2e</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Table-Driven Testing in Typescript</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Tue, 21 Jan 2025 10:22:15 +0000</pubDate>
      <link>https://dev.to/samuelko123/table-driven-testing-in-typescript-4154</link>
      <guid>https://dev.to/samuelko123/table-driven-testing-in-typescript-4154</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Table-Driven Testing, also known as &lt;a href="https://en.wikipedia.org/wiki/Data-driven_testing" rel="noopener noreferrer"&gt;Data-Driven Testing&lt;/a&gt; and &lt;a href="https://www.baeldung.com/parameterized-tests-junit-5" rel="noopener noreferrer"&gt;Parameterized Test&lt;/a&gt;, is a popular methodology in Golang.&lt;/p&gt;

&lt;p&gt;The idea is to run several sets of input data against the same logic, in order to increase test coverage. 💡&lt;/p&gt;

&lt;p&gt;Let's look at an example in Typescript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;The unit test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;temperature converter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="s2"&gt;`
    celcius | fahrenheit
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;148&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;    | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  `&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;converts $celcius degrees C to $fahrenheit degrees F&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fahrenheit&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertCToF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test result:&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhf45sdgmpg6n3v0bbllm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhf45sdgmpg6n3v0bbllm.png" alt="Screenshot of test result" width="800" height="148"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Explanation
&lt;/h3&gt;

&lt;p&gt;The test utilises &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates" rel="noopener noreferrer"&gt;Tagged Templates&lt;/a&gt; to run several sets of arguments against the same function.&lt;/p&gt;

&lt;p&gt;If you're using VS Code, you can install &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; to format the table for you. 🚀&lt;/p&gt;
&lt;h3&gt;
  
  
  Benefits of Table-Driven Testing
&lt;/h3&gt;
&lt;h4&gt;
  
  
  1. Reduce repetition
&lt;/h4&gt;

&lt;p&gt;Table-Driven Testing not only reduces &lt;a href="https://en.wikipedia.org/wiki/Source_lines_of_code" rel="noopener noreferrer"&gt;LOC&lt;/a&gt; in unit tests, but it also saves us from writing test descriptions for each of the test cases.&lt;/p&gt;
&lt;h4&gt;
  
  
  2. Enhance visibility
&lt;/h4&gt;

&lt;p&gt;Table-Driven Testing groups the data at the top, enabling us to compare against the acceptance criteria. It also helps us to identify edge-case scenarios. 🔍&lt;/p&gt;
&lt;h4&gt;
  
  
  3. Enable integration with real-life data
&lt;/h4&gt;

&lt;p&gt;It's possible to supply an array of data to &lt;code&gt;it.each&lt;/code&gt;. It means we could potentially create an automated system that commit sample production data as a json file to the repository for unit testing purpose.&lt;/p&gt;

&lt;p&gt;Below is an example of &lt;code&gt;it.each&lt;/code&gt; using an array as input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;temperature converter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testCases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;148&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;212&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testCases&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;converts $celcius degrees C to $fahrenheit degrees F&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fahrenheit&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertCToF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Limitations of Table-Driven Testing
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Harder to debug
&lt;/h4&gt;

&lt;p&gt;When one of the test cases failed in the table, we would need to comment out other test cases to debug.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Not as descriptive
&lt;/h4&gt;

&lt;p&gt;A test case should be validating one particular scenario, e.g. positive value, negative value etc.&lt;/p&gt;

&lt;p&gt;Without a meaningful description, it can be hard to understand the purpose of each test case.&lt;/p&gt;

&lt;p&gt;To address that, we can add a text description column.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. The table becomes too big
&lt;/h4&gt;

&lt;p&gt;When a table has 4 columns of input and 1 column of output, it can become harder to understand the behaviour by purely reviewing the unit test. 😵&lt;/p&gt;

&lt;p&gt;In that case, consider using smaller tables to describe individual behaviours, or refactor the function to receive less inputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Bad Example
&lt;/h3&gt;

&lt;p&gt;When we start using Table-Driven Testing, it's tempting to put every possible outcome into one big table.&lt;/p&gt;

&lt;p&gt;And worse still, we may want to put conditionals into the test script. ❌&lt;/p&gt;

&lt;p&gt;Consider the test below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;temperature converter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;each&lt;/span&gt;&lt;span class="s2"&gt;`
    celcius | fahrenheit | error
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;    | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;celcius cannot be below 273.15&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;148&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;    | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;    | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;      | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;  | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="mi"&gt;212&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;     | &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
  `&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;converts $celcius degrees C to $fahrenheit degrees F or throws error $error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;error&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nf"&gt;convertCToF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;convertCToF&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;celcius&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fahrenheit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It has a few issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The table has holes. It means we have to consider &lt;code&gt;null&lt;/code&gt; when writing the test.&lt;/li&gt;
&lt;li&gt;if we need to debug a test case, we would need to determine which code path it takes within the unit test first.&lt;/li&gt;
&lt;li&gt;The test script becomes much harder to read.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The better solution is to write a separate test for &lt;code&gt;throws error if celcius is below 273.15&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Table-Driven Testing is a useful tool to increase test coverage. If used wisely, it can improve readability as well.&lt;/p&gt;




&lt;p&gt;Thank you for reading.&lt;br&gt;
Hope you find this post interesting!&lt;/p&gt;

&lt;p&gt;Have you used table-driven testing before?&lt;br&gt;
Share your experience in the comments!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>typescript</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>A case study of creating configurable values</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Fri, 17 Jan 2025 12:07:01 +0000</pubDate>
      <link>https://dev.to/samuelko123/a-case-study-of-creating-configurable-values-1oa3</link>
      <guid>https://dev.to/samuelko123/a-case-study-of-creating-configurable-values-1oa3</guid>
      <description>&lt;h3&gt;
  
  
  Glossary
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Data_access_object" rel="noopener noreferrer"&gt;DAO&lt;/a&gt; - Data access object&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Case study
&lt;/h3&gt;

&lt;p&gt;Once upon a time, there was a process within a monolithic enterprise system.&lt;/p&gt;

&lt;p&gt;Its purpose was to receive product codes from external API, query the database, and send product data to another external API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn99cspcr96eaecz03vi6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn99cspcr96eaecz03vi6.png" alt="System diagram" width="374" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml Example Process
skinparam componentStyle rectangle

database "Product Table"

rectangle "Process" {
  [Admin UI]
  [Service]
  [DAO]
}

[Admin UI] --&amp;gt; Service: clicks to run
[API 1] -&amp;gt; [Service] : product\ncodes
[Service] -&amp;gt; [API 2] : products
[Service] -&amp;gt; [DAO] : product\ncodes
[Service] &amp;lt;-- [DAO] : products

[DAO] --&amp;gt; [Product Table] : database query
@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;h3&gt;
  
  
  The new requirement
&lt;/h3&gt;

&lt;p&gt;We want to divide the product codes into batches, so that the database query wouldn't be too big.&lt;/p&gt;

&lt;h3&gt;
  
  
  The proposed solutions
&lt;/h3&gt;

&lt;p&gt;Obviously, there would be a value "number of products per batch".&lt;/p&gt;

&lt;h4&gt;
  
  
  1. The configurable solution
&lt;/h4&gt;

&lt;p&gt;There is a school of thought - "Data should be separated from logic".&lt;/p&gt;

&lt;p&gt;Following this thought, the value should be configurable from Admin UI, and subsequently passed down to DAO.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbkb9qjdihfvl3u5uj2b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsbkb9qjdihfvl3u5uj2b.png" alt="System diagram" width="119" height="291"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml Example Process
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

[Admin UI] as "Admin UI\n(with **value**)"
[Service]
[DAO]

[Admin UI] --&amp;gt; Service: value
[Service] --&amp;gt; [DAO] : value

@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;In this scenario, a few things could happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A system user accidentally updates the value to a non-sensible value, breaking the system.&lt;/li&gt;
&lt;li&gt;Another requirement comes in to ensure that only developers can configure the value from the UI.&lt;/li&gt;
&lt;li&gt;With hundreds of configurations, the system user would have a hard time finding the configuration that they can really update.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the flip side, this approach does have an advantage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We don't need to deploy code changes to adjust the value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach is suitable for an environment where deployment is infrequent.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. The environment variable solution
&lt;/h4&gt;

&lt;p&gt;Given it's not an user-facing configuration, another approach would be to store the value in environment variable.&lt;/p&gt;

&lt;p&gt;The DAO will directly obtain the value from environment variable and use it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9kabn0kcws4a8runwjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx9kabn0kcws4a8runwjq.png" alt="System diagram" width="322" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml Example Process
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

[Admin UI]
[Service]
[DAO]
[Environment Variable]

[Admin UI] -.&amp;gt; Service
[Service] -.&amp;gt; [DAO]
[Environment Variable] -&amp;gt; DAO : value

@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;It's a better approach because Service and Cron Job don't need to know anything about "Number of products per batch", which is purely a concept in application logic (as opposed to business logic).&lt;/p&gt;

&lt;p&gt;In this approach, a few things could happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The code passes QA environment easily because the value is lower.&lt;/li&gt;
&lt;li&gt;The code fails in Prod environment because the value is higher.&lt;/li&gt;
&lt;li&gt;A lot of work is required to ensure environment consistency because some environment variables do vary between environments, such as API URLs.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  3. The hard-coding solution
&lt;/h4&gt;

&lt;p&gt;The value is only used by DAO.&lt;br&gt;
The value should not change across environment.&lt;br&gt;
Therefore, the best place for it is within the DAO itself.&lt;/p&gt;

&lt;p&gt;It is version-controlled.&lt;br&gt;
If someone changes it and breaks the system, we will know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Configurable values mean maintenance and potential inconsistency.&lt;/p&gt;

&lt;p&gt;Create configurable values only when there is a real need.&lt;/p&gt;




&lt;p&gt;Thank you for reading.&lt;br&gt;
Feel free to share your thoughts in the comments! 😉&lt;/p&gt;

</description>
      <category>programming</category>
      <category>productivity</category>
      <category>backend</category>
      <category>learning</category>
    </item>
    <item>
      <title>Review your own pull request (PR)</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Mon, 13 Jan 2025 10:36:41 +0000</pubDate>
      <link>https://dev.to/samuelko123/review-your-own-pull-request-pr-f3i</link>
      <guid>https://dev.to/samuelko123/review-your-own-pull-request-pr-f3i</guid>
      <description>&lt;p&gt;Once upon a time, a developer created a PR and requested for review.&lt;/p&gt;

&lt;p&gt;Little did he know:&lt;br&gt;
❌ He misspelt a variable name.&lt;br&gt;
❌ He forgot to remove a &lt;code&gt;console.log("testing")&lt;/code&gt;.&lt;br&gt;
❌ He overlooked a &lt;code&gt;// TODO: refactor this code&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As such, it took several rounds of review to get it across the line.&lt;/p&gt;

&lt;p&gt;Had he reviewed his PR first, he could've avoided this disaster.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a PR?
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://www.atlassian.com/git/tutorials/making-a-pull-request" rel="noopener noreferrer"&gt;PR&lt;/a&gt; is basically a request of "May I put some code changes into your branch?".&lt;/p&gt;

&lt;p&gt;In a collaborated environment, the main branch is usually protected. It means that you need to create a PR and obtain an approval to contribute your code changes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why should I review my own PR?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q0ew34m6d6w17aribzf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6q0ew34m6d6w17aribzf.jpg" alt="Can you please approve my pull request?" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One may say, "I know my code and it works. Why should I review my own PR?"&lt;/p&gt;

&lt;p&gt;To answer that, let me introduce a new made-up word - &lt;strong&gt;Approvability&lt;/strong&gt;. 🚀&lt;/p&gt;

&lt;p&gt;Achieving "It works." is the first step of creating a PR. Getting someone to approve it is the goal.&lt;/p&gt;

&lt;p&gt;From a reviewer's point of view, it's much easier to approve an easy-to-follow PR than one that exhausts their brain power with ambiguous function names and obscure logic.&lt;/p&gt;

&lt;p&gt;Apart from approvability, self-review can:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Eliminate stupid mistakes
&lt;/h4&gt;

&lt;p&gt;Trivial mistakes like typos and code misalignment can make yourself look unprofessional.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Enhance readability
&lt;/h4&gt;

&lt;p&gt;If you have a hard time reading your code, the reviewer will have an even harder time reviewing your code.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Improve code quality
&lt;/h4&gt;

&lt;p&gt;When you review your own code changes from a bird's eye view, you may come up with refactoring ideas. 💡&lt;/p&gt;

&lt;h3&gt;
  
  
  How should I review my own PR?
&lt;/h3&gt;

&lt;p&gt;Review it like a PR from someone else.&lt;/p&gt;

&lt;p&gt;Ask yourself:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Does it satisfy the requirements?
&lt;/h4&gt;

&lt;p&gt;When we're so focused to make the code work, we could miss some of the requirements.&lt;/p&gt;

&lt;p&gt;We don't want the tester to come to us and say "It doesn't work!", right?&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Does it exceed the requirements?
&lt;/h4&gt;

&lt;p&gt;When we code, we may see things that we want to refactor (or fix). Very often, those refactoring are unrelated to the requirements.&lt;/p&gt;

&lt;p&gt;It's called &lt;a href="https://en.wikipedia.org/wiki/Scope_creep" rel="noopener noreferrer"&gt;scope creep&lt;/a&gt;. The result is a PR with two sets of changes entangled together, making it harder to review.&lt;/p&gt;

&lt;p&gt;When it happens, consider splitting the additional code changes to another PR. It will make your reviewer's life easier. ☺️&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Do I need to think twice to understand the code?
&lt;/h4&gt;

&lt;p&gt;If yes, it means that the reviewer will need to think 3+ times.&lt;/p&gt;

&lt;p&gt;When it happens, we have 3 options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Refactor the code to make it more readable&lt;/li&gt;
&lt;li&gt;Use code comment to explain the "why" (but not the "what")&lt;/li&gt;
&lt;li&gt;Use PR comment to invite discussions&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4. Does the commit history make sense?
&lt;/h4&gt;

&lt;p&gt;Reviewers may rely on commit history to follow your train of thoughts.&lt;/p&gt;

&lt;p&gt;Before requesting for review, you have the opportunity to tidy up the commit history.&lt;/p&gt;

&lt;p&gt;After requesting for review, do not &lt;a href="https://www.git-tower.com/learn/git/faq/git-force-push" rel="noopener noreferrer"&gt;force push&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Thank you for reading.&lt;br&gt;
Hope you find this post interesting!&lt;/p&gt;

&lt;p&gt;How do you review your own PR?&lt;br&gt;
Share your experience in the comments!&lt;/p&gt;

</description>
      <category>programming</category>
      <category>softwaredevelopment</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>An interesting observation on C# code coverage</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Fri, 10 Jan 2025 11:58:49 +0000</pubDate>
      <link>https://dev.to/samuelko123/an-interesting-observation-on-c-code-coverage-5hl2</link>
      <guid>https://dev.to/samuelko123/an-interesting-observation-on-c-code-coverage-5hl2</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Code_coverage" rel="noopener noreferrer"&gt;Code coverage&lt;/a&gt; is an indicator, not an metric.&lt;/p&gt;

&lt;p&gt;In this post, I will share an example for your entertainment.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Animal&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;CanTalk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;CanWalk&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;init&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnimalValidator&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanTalk&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanWalk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The good test
&lt;/h2&gt;

&lt;p&gt;Let's test all the input combinations.&lt;br&gt;
It gives us 100% code coverage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AnimalValidatorTest&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IsHuman_WhenAnimalCanTalkAndCanWalk_ReturnsTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;canTalk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;canWalk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;CanTalk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canTalk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CanWalk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canWalk&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isHuman&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AnimalValidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;True&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isHuman&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;IsHuman_WhenAnimalCannotTalkOrCannotWalk_ReturnsFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;canTalk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;canWalk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;CanTalk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canTalk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CanWalk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canWalk&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;isHuman&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AnimalValidator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IsHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;Assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;False&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isHuman&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The 100% coverage test
&lt;/h2&gt;

&lt;p&gt;One day, someone removed two test cases &lt;del&gt;because they thought that the tests were running for too long&lt;/del&gt; .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Theory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;// [InlineData(false, false)]&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;InlineData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="c1"&gt;// [InlineData(true, false)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interestingly, we still get 100% code coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  The explanation
&lt;/h2&gt;

&lt;p&gt;Let's put the code into &lt;a href="https://sharplab.io/" rel="noopener noreferrer"&gt;SharpLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanTalk&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanWalk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;will compile into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanTalk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanWalk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It means:&lt;br&gt;
&lt;code&gt;CanTalk = true&lt;/code&gt; contributes 50%.&lt;br&gt;
&lt;code&gt;CanTalk = false&lt;/code&gt; contributes 50%.&lt;/p&gt;

&lt;p&gt;The value of &lt;code&gt;CanWalk&lt;/code&gt; doesn't matter.&lt;/p&gt;
&lt;h2&gt;
  
  
  The brittleness of 100% coverage test
&lt;/h2&gt;

&lt;p&gt;Another day, someone changed the order of boolean &lt;del&gt;because they thought it would improve performance because majority of animals cannot walk&lt;/del&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;IsHuman&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Animal&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// return animal.CanTalk &amp;amp;&amp;amp; animal.CanWalk;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanWalk&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;animal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CanTalk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, our test coverage drops from 100% to 50%, because both of our test cases are &lt;code&gt;CanWalk = true&lt;/code&gt;.&lt;/p&gt;

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

&lt;p&gt;C# code coverage is based on compiled code.&lt;/p&gt;

&lt;p&gt;Writing good unit tests is more important than code coverage.&lt;/p&gt;

&lt;p&gt;Hope you find it interesting.&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>programming</category>
      <category>testing</category>
    </item>
    <item>
      <title>An example of Consumer-Driven Development</title>
      <dc:creator>Samuel Ko</dc:creator>
      <pubDate>Tue, 07 Jan 2025 07:55:25 +0000</pubDate>
      <link>https://dev.to/samuelko123/consumer-driven-development-3k87</link>
      <guid>https://dev.to/samuelko123/consumer-driven-development-3k87</guid>
      <description>&lt;p&gt;It's my first time sharing my experience.&lt;br&gt;
Let me know if there's anything I can improve.&lt;/p&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;"Consumer-Driven Development" is a made-up terminology.&lt;/p&gt;

&lt;p&gt;In this mindset, we define what the "consumer" needs first, then orchestrate the application to satisfy the need. &lt;/p&gt;

&lt;p&gt;The "consumer" here can be perceived as the presentation layer, such as API controllers.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;
&lt;h3&gt;
  
  
  The existing architecture
&lt;/h3&gt;

&lt;p&gt;We have an application.&lt;br&gt;
It calls an API endpoint periodically and logs the data:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktxy7tzgnpe9l5f9dr3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fktxy7tzgnpe9l5f9dr3k.png" alt="Component Diagram" width="403" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The diagram is drawn using &lt;a href="https://plantuml.com/" rel="noopener noreferrer"&gt;PlantUML&lt;/a&gt;.&lt;br&gt;

  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml Example App
skinparam componentStyle rectangle

cloud "API Endpoint"
cloud "Elastic"

rectangle "Application" {
  [Gateway]
  [API Client]
  [Repository]
  [Cron Job]
}

[API Endpoint] -&amp;gt; [Gateway]
[Gateway] --&amp;gt; [API Client] : HTTP Response
[API Client] --&amp;gt; [Repository] : Product DTO
[Repository] --&amp;gt; [Cron Job] : Product

[Cron Job] -&amp;gt; [Elastic] : Logs

@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;h3&gt;
  
  
  The new business requirement
&lt;/h3&gt;

&lt;p&gt;The API endpoint provides a field called &lt;code&gt;status&lt;/code&gt;.&lt;br&gt;
It can be &lt;code&gt;Approved&lt;/code&gt;, &lt;code&gt;Disapproved&lt;/code&gt;, &lt;code&gt;Pending&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We want to log the total number of &lt;code&gt;Approved&lt;/code&gt; products.&lt;/p&gt;
&lt;h3&gt;
  
  
  Solution #1 - The data-flow approach
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrunjys0i333sz8dbxja.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmrunjys0i333sz8dbxja.png" alt="Conceptual diagram" width="468" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

component [Repository] as "**Step 3**\nRepository"
component [CronJob] as "**Step 4**\nCronJob"

[API Client] -&amp;gt; [Repository] : **Step 1**\nProductDTO
[Repository] -&amp;gt; [CronJob] : **Step 2**\nProduct

@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt;&lt;br&gt;
We want to obtain the data from the API endpoint.&lt;br&gt;
Therefore, we create &lt;code&gt;ProductDTO.Status&lt;/code&gt; as a text field.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt;&lt;br&gt;
We want meaningful data in our domain object.&lt;br&gt;
Therefore, we create &lt;code&gt;Product.Status&lt;/code&gt; as an enum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Approved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Disapproved&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt;&lt;br&gt;
Let &lt;code&gt;Repository&lt;/code&gt; do the translation.&lt;br&gt;
(Note: Exception handling is omitted.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productDtos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dto&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Enum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parse&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt;&lt;br&gt;
Finally, we can log the number of approved products in &lt;code&gt;CronJob&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Number of approved products: {ApprovedProductCount}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;Status&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Approved&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Solution #2 - The consumer-driven approach
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp30heqsl1mwqbapze8yx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp30heqsl1mwqbapze8yx.png" alt="Conceptual diagram" width="468" height="72"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;
  PlantUML Text
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@startuml
skinparam componentStyle rectangle
skinparam defaultTextAlignment center

component [Repository] as "**Step 2**\nRepository"
component [CronJob] as "**Step 1**\nCronJob"

[API Client] -&amp;gt; [Repository] : **Step 2**\nProductDTO
[Repository] -&amp;gt; [CronJob] : **Step 1**\nProduct

@enduml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt;&lt;br&gt;
The consumer, &lt;code&gt;CronJob&lt;/code&gt;, needs to know whether a product is approved. &lt;br&gt;
Therefore, we create &lt;code&gt;Product.IsApproved&lt;/code&gt; as a boolean field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Information&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"Number of approved products: {ApprovedProductCount}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsApproved&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt;&lt;br&gt;
The provider, &lt;code&gt;Repository&lt;/code&gt;, will figure out how to populate the new &lt;code&gt;IsApproved&lt;/code&gt; field.&lt;br&gt;
To do so, we create &lt;code&gt;ProductDTO.Status&lt;/code&gt; as a text field.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productDtos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dto&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;IsApproved&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Status&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"Approved"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Result
&lt;/h3&gt;

&lt;p&gt;In solution #1, the enum value &lt;code&gt;Status.Disapproved&lt;/code&gt; and &lt;code&gt;Status.Pending&lt;/code&gt; are unused. We spent extra effort to handle exceptions when translating the text field into enum.&lt;/p&gt;

&lt;p&gt;In solution #2, the use of boolean field makes the code much simpler. One could argue that it lacks data validation and scalability, but that is not within the requirement for today.&lt;/p&gt;

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

&lt;p&gt;Using Consumer-Driven Development, we ensure that our effort is spent to meet the requirement, and nothing else.&lt;/p&gt;

&lt;p&gt;Happy to hear your thoughts. 😉&lt;/p&gt;

</description>
      <category>programming</category>
      <category>backend</category>
      <category>productivity</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
