<?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: Ujjwal Kumar Singh</title>
    <description>The latest articles on DEV Community by Ujjwal Kumar Singh (@beinghumantester).</description>
    <link>https://dev.to/beinghumantester</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%2F754236%2F76bdcb36-a466-493b-ace0-97b50a9f1909.png</url>
      <title>DEV Community: Ujjwal Kumar Singh</title>
      <link>https://dev.to/beinghumantester</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/beinghumantester"/>
    <language>en</language>
    <item>
      <title>Python for Testers : Validating a JSON API Response Without Any Framework</title>
      <dc:creator>Ujjwal Kumar Singh</dc:creator>
      <pubDate>Tue, 21 Jul 2026 16:27:25 +0000</pubDate>
      <link>https://dev.to/beinghumantester/python-for-testers-validating-a-json-api-response-without-any-framework-13en</link>
      <guid>https://dev.to/beinghumantester/python-for-testers-validating-a-json-api-response-without-any-framework-13en</guid>
      <description>&lt;p&gt;Most Testers validate API responses inside a test framework. But do we know how to do it with just Python?&lt;/p&gt;

&lt;p&gt;Scenario: Our API returns a JSON response after creating a user. We need to verify the status, the message, and the user details are exactly what we expected.&lt;/p&gt;

&lt;p&gt;Here is the JSON response:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;JSON data: &lt;br&gt;
*&lt;/em&gt;&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="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"success"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user"&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;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;101&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Ujjwal"&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;Here is the Python validation:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Python code: &lt;br&gt;
*&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ujjwal&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&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;101&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validation Passed&lt;/span&gt;&lt;span class="sh"&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;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Validation Failed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Validation Passed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;****Why this matters as a Tester:&lt;/p&gt;




&lt;p&gt;Before we reach for requests or pytest, understanding how Python reads and validates JSON at the base level makes us a sharper tester. Every API assertion we write in a framework is doing exactly this under the hood.&lt;/p&gt;

&lt;p&gt;Knowing the foundation makes us better at debugging when the framework gives us a result we did not expect.&lt;/p&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/beinghumantester/Fundamental-Coding-Programs" rel="noopener noreferrer"&gt;https://github.com/beinghumantester/Fundamental-Coding-Programs&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Python for Testers: Finding Unique Status Values from an API Response String</title>
      <dc:creator>Ujjwal Kumar Singh</dc:creator>
      <pubDate>Mon, 20 Jul 2026 17:47:42 +0000</pubDate>
      <link>https://dev.to/beinghumantester/python-for-testers-finding-unique-status-values-from-an-api-response-string-385b</link>
      <guid>https://dev.to/beinghumantester/python-for-testers-finding-unique-status-values-from-an-api-response-string-385b</guid>
      <description>&lt;p&gt;Ever got a concatenated API response string and needed to know what unique status values were present?&lt;/p&gt;

&lt;p&gt;Scenario: our API returns a long string of status codes like "Automation and Python" and we need to extract every unique status that appeared, in the order it first showed up.&lt;/p&gt;

&lt;p&gt;Here is the Python code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;api_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Automation and Python&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;api_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;api_response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;ch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No library. No regex. Just Python iterating through the string and tracking what it has already seen.&lt;/p&gt;

&lt;p&gt;Why this matters as a Tester:&lt;/p&gt;

&lt;p&gt;Not every API response comes in a clean, structured format. Sometimes you are dealing with concatenated strings, log outputs, or raw response bodies. Knowing how to extract unique values from messy string data is a practical skill that shows up more often than we would expect in real test automation work.&lt;/p&gt;

</description>
      <category>python</category>
      <category>testing</category>
      <category>selenium</category>
      <category>automation</category>
    </item>
    <item>
      <title>Today’s Python Challenge for Testers: Deduplicating API Response Values Before Assertion</title>
      <dc:creator>Ujjwal Kumar Singh</dc:creator>
      <pubDate>Sun, 19 Jul 2026 06:24:33 +0000</pubDate>
      <link>https://dev.to/beinghumantester/todays-python-challenge-for-testers-deduplicating-api-response-values-before-assertion-ko5</link>
      <guid>https://dev.to/beinghumantester/todays-python-challenge-for-testers-deduplicating-api-response-values-before-assertion-ko5</guid>
      <description>&lt;p&gt;Have we ever received an API response with duplicate values and our assertion failed, not because the data was wrong, but because duplicates were messing up our comparison?&lt;/p&gt;

&lt;p&gt;Scenario: our API returns a list of user IDs, but some IDs appear more than once. Before we assert, we need a clean, unique list.&lt;/p&gt;

&lt;p&gt;Here is the Python code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;unique_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;unique_values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;unique_values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unique_values&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Output:

[1, 2, 3, 4, 5, 7, 8, 9]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Duplicate data is removed. Now our assertion runs against clean data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why this matters as a Tester:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Asserting against raw API responses without cleaning the data first is a common source of false failures. Understanding how deduplication works at the code level makes us a better judge of when to clean data and when the duplicate itself is the bug.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/beinghumantester/Fundamental-Coding-Programs" rel="noopener noreferrer"&gt;Github Repo Link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>selenium</category>
      <category>python</category>
      <category>coding</category>
    </item>
    <item>
      <title>Python for SDETs: Comparing Two API Response Arrays</title>
      <dc:creator>Ujjwal Kumar Singh</dc:creator>
      <pubDate>Sat, 18 Jul 2026 15:42:03 +0000</pubDate>
      <link>https://dev.to/beinghumantester/python-for-sdets-comparing-two-api-response-arrays-lig</link>
      <guid>https://dev.to/beinghumantester/python-for-sdets-comparing-two-api-response-arrays-lig</guid>
      <description>&lt;p&gt;Ever got two API responses and needed to know what matched?&lt;br&gt;
Classic scenario — your API returns a list of user IDs. Your expected list has what should be there. You need to know what actually matched and how many.&lt;br&gt;
Here is the Python pattern I use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;17&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;matched&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Number of matching elements:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matched&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;*&lt;em&gt;Output *&lt;/em&gt;&lt;br&gt;
Number of matching elements: 2&lt;br&gt;
[14, 16]&lt;/p&gt;

&lt;p&gt;Two values matched. Everything else is either missing or unexpected, both of which are bugs worth reporting.&lt;/p&gt;

</description>
      <category>python</category>
      <category>automation</category>
      <category>testing</category>
      <category>selenium</category>
    </item>
    <item>
      <title>Weekly Challenge-3: Building an AI-Powered Open Source Contribution Assistant with n8n</title>
      <dc:creator>Ujjwal Kumar Singh</dc:creator>
      <pubDate>Mon, 22 Jun 2026 01:45:02 +0000</pubDate>
      <link>https://dev.to/beinghumantester/building-an-ai-powered-open-source-contribution-assistant-with-n8n-1ljn</link>
      <guid>https://dev.to/beinghumantester/building-an-ai-powered-open-source-contribution-assistant-with-n8n-1ljn</guid>
      <description>&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;br&gt;
One challenge in open source is not the lack of opportunities, it’s deciding which opportunity to work on next.&lt;/p&gt;

&lt;p&gt;Many repositories have hundreds of open issues. Some are beginner-friendly, some require deep project knowledge, and others may no longer be relevant. As contributors, we often spend a significant amount of time reviewing issues before making a contribution.&lt;/p&gt;

&lt;p&gt;To explore how automation could help, I built an AI-powered contribution assistant using n8n.&lt;/p&gt;

&lt;p&gt;The goal was to automatically collect issues, evaluate them against predefined criteria, prioritize them, and generate a daily contribution digest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
When reviewing open source repositories, contributors often face questions such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which issue matches my current skill set?&lt;/li&gt;
&lt;li&gt;Which issue can be worked on within the time I have available?&lt;/li&gt;
&lt;li&gt;Which issue provides the best learning opportunity?&lt;/li&gt;
&lt;li&gt;Which issue has a high probability of resulting in a merged contribution?&lt;/li&gt;
&lt;li&gt;Which issue should I start with today?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Answering these questions manually across multiple repositories can become time-consuming.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Solution&lt;/strong&gt;&lt;br&gt;
The workflow automates issue discovery and prioritization.&lt;/p&gt;

&lt;p&gt;It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Retrieves open issues from GitHub repositories&lt;/li&gt;
&lt;li&gt;Filters out pull requests&lt;/li&gt;
&lt;li&gt;Collects issue metadata&lt;/li&gt;
&lt;li&gt;Uses an LLM to analyze and rank opportunities&lt;/li&gt;
&lt;li&gt;Generates a daily digest&lt;/li&gt;
&lt;li&gt;Stores recommendations in Notion for future tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The objective is not to replace contributor judgment, but to reduce the effort required to identify promising contribution opportunities.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhpdts927321o9ez90ron.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhpdts927321o9ez90ron.png" alt=" " width="799" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workflow Overview&lt;/strong&gt;&lt;br&gt;
The workflow consists of four major stages:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Issue Collection&lt;/strong&gt;&lt;br&gt;
Issues are fetched from GitHub repositories.&lt;/p&gt;

&lt;p&gt;Relevant information is extracted, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Labels&lt;/li&gt;
&lt;li&gt;Comment count&lt;/li&gt;
&lt;li&gt;Last update time&lt;/li&gt;
&lt;li&gt;Repository source&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. Issue Evaluation&lt;/strong&gt;&lt;br&gt;
An LLM evaluates issues against predefined criteria such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technology relevance&lt;/li&gt;
&lt;li&gt;Documentation relevance&lt;/li&gt;
&lt;li&gt;Learning value&lt;/li&gt;
&lt;li&gt;Expected effort&lt;/li&gt;
&lt;li&gt;Probability of successful contribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3. Recommendation Generation&lt;/strong&gt;&lt;br&gt;
The workflow generates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Top contribution opportunities&lt;/li&gt;
&lt;li&gt;Quick wins&lt;/li&gt;
&lt;li&gt;Learning-focused opportunities&lt;/li&gt;
&lt;li&gt;A recommended issue to start with&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Knowledge Management&lt;/strong&gt;&lt;br&gt;
Recommendations are stored in Notion and delivered via email.&lt;/p&gt;

&lt;p&gt;This creates a searchable backlog instead of requiring contributors to revisit repositories repeatedly.&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4wdyqr58fslw75mc3h9w.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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4wdyqr58fslw75mc3h9w.png" alt=" " width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Reduced Decision Fatigue&lt;/strong&gt;&lt;br&gt;
Contributors spend less time searching and more time contributing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consistent Engagement&lt;/strong&gt;&lt;br&gt;
A daily recommendation can encourage regular participation in open source projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Personalized Prioritization&lt;/strong&gt;&lt;br&gt;
Different contributors value different things. The ranking criteria can be adjusted based on individual goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Better Visibility&lt;/strong&gt;&lt;br&gt;
Storing recommendations in Notion makes it easier to track contribution opportunities over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Challenges Encountered&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building the workflow surfaced several practical challenges:&lt;/li&gt;
&lt;li&gt;Prompt design significantly impacted recommendation quality.&lt;/li&gt;
&lt;li&gt;Structured JSON outputs were necessary for reliable automation.&lt;/li&gt;
&lt;li&gt;Notion schema mismatches caused workflow failures.&lt;/li&gt;
&lt;li&gt;LLM-generated outputs required validation before downstream processing.&lt;/li&gt;
&lt;li&gt;Integration testing took considerably longer than expected.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow itself was relatively straightforward. Making the components work reliably together required the most effort.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible Improvements&lt;/strong&gt;&lt;br&gt;
The current workflow works well as a personal assistant, but there are several areas for improvement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Historical Tracking&lt;/strong&gt;&lt;br&gt;
Track previously recommended issues and avoid recommending the same issue repeatedly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Contribution Feedback Loop&lt;/strong&gt;&lt;br&gt;
Record completed contributions and use that information to improve future recommendations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skill-Based Profiles&lt;/strong&gt;&lt;br&gt;
Allow contributors to define profiles such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation Contributor&lt;/li&gt;
&lt;li&gt;Automation Engineer&lt;/li&gt;
&lt;li&gt;Backend Developer&lt;/li&gt;
&lt;li&gt;Frontend Developer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and generate recommendations accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-Repository Support&lt;/strong&gt;&lt;br&gt;
Expand beyond a single ecosystem and evaluate opportunities across multiple projects.&lt;/p&gt;

&lt;p&gt;**Effort Estimation&lt;br&gt;
**Use issue history and metadata to estimate actual effort more accurately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duplicate Detection&lt;/strong&gt;&lt;br&gt;
Avoid recommending issues that are very similar or represent the same underlying work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Weekly and Monthly Reports&lt;/strong&gt;&lt;br&gt;
Generate summaries that show:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Issues reviewed&lt;/li&gt;
&lt;li&gt;Contributions completed&lt;/li&gt;
&lt;li&gt;Skills explored&lt;/li&gt;
&lt;li&gt;Areas of growth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaway&lt;/strong&gt;&lt;br&gt;
The most interesting lesson from this experiment was that the bottleneck was not finding open source work, it was selecting the right work.&lt;/p&gt;

&lt;p&gt;Automating issue discovery and prioritization helped transform a large list of open issues into a smaller set of actionable recommendations.&lt;/p&gt;

&lt;p&gt;While the workflow is still evolving, it demonstrates how automation and AI can be used to reduce overhead and help contributors spend more time learning, building, and contributing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/beinghumantester/n8n_selenium_contribution_agent" rel="noopener noreferrer"&gt;Github Repo Link&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
