<?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: Henrique Sagara</title>
    <description>The latest articles on DEV Community by Henrique Sagara (@htsagara).</description>
    <link>https://dev.to/htsagara</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%2F2017126%2F11bd4584-6f67-49b2-a12b-e9b2c7ab82af.jpeg</url>
      <title>DEV Community: Henrique Sagara</title>
      <link>https://dev.to/htsagara</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/htsagara"/>
    <language>en</language>
    <item>
      <title>Life After Graduation: A New Chapter</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Sat, 29 Mar 2025 16:11:17 +0000</pubDate>
      <link>https://dev.to/htsagara/life-after-graduation-a-new-chapter-50nk</link>
      <guid>https://dev.to/htsagara/life-after-graduation-a-new-chapter-50nk</guid>
      <description>&lt;p&gt;It’s been a while since I’ve written anything here. This blog has mostly been dedicated to sharing my open-source contributions. However, due to a few things that have been happening in my life, I’ve stepped away from contributing for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Post-Grad Life
&lt;/h2&gt;

&lt;p&gt;Since graduating in December 2024, I’ve been actively applying for software developer roles. Unfortunately, the job market has been quite tough for recent grads.&lt;/p&gt;

&lt;p&gt;I was lucky enough to land a remote developer job—but I was laid off during my very first week. The reason? Management expected me to have more experience than I actually did. It was a tough moment, but also a valuable lesson in how unpredictable this industry can be.&lt;/p&gt;

&lt;h2&gt;
  
  
  After the Layoff
&lt;/h2&gt;

&lt;p&gt;Since then, I’ve been focusing on a side project I started with a friend during our final year of college. The project got accepted into our college’s startup incubator, which was incredibly exciting and gave us a real sense of momentum.&lt;/p&gt;

&lt;p&gt;I’ve also continued job hunting, but it’s been a bit discouraging. I’ve been applying through the usual platforms—LinkedIn, Indeed, Glassdoor—and it’s frustrating to receive the same “Unfortunately…” email over and over. Sometimes recruiters reach out to me on LinkedIn, but after I express interest, they just disappear without a word. It’s draining, but I know I’m not alone in this experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Next
&lt;/h2&gt;

&lt;p&gt;I’ve decided to go all-in on the project with my friend. It’s something we’re building together from the ground up, and I’m genuinely excited about it. I’ll be sharing updates on our journey here—not technical details, but the ups and downs of building something of our own.&lt;/p&gt;

&lt;p&gt;My hope is that by sharing this experience, it might resonate with someone else out there trying to figure things out after graduation. Maybe even inspire someone to start their own thing too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I’m also open to feedback, constructive criticism, or suggestions—feel free to leave a comment below!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>devchallenge</category>
      <category>beginners</category>
      <category>newbie</category>
    </item>
    <item>
      <title>Building a Digital Signage Content Management System: A Challenge and a Journey</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Tue, 17 Dec 2024 20:44:06 +0000</pubDate>
      <link>https://dev.to/htsagara/building-a-digital-signage-content-management-system-a-challenge-and-a-journey-43m</link>
      <guid>https://dev.to/htsagara/building-a-digital-signage-content-management-system-a-challenge-and-a-journey-43m</guid>
      <description>&lt;h3&gt;
  
  
  Building a Digital Signage Content Management System: A Challenge and a Journey
&lt;/h3&gt;

&lt;p&gt;Recently, I completed an exciting code challenge for a media company specializing in digital kiosks. The task? To create a &lt;strong&gt;Digital Signage Content Management System&lt;/strong&gt; that connects a &lt;strong&gt;web application&lt;/strong&gt; with a &lt;strong&gt;desktop display app&lt;/strong&gt; using WebSockets for communication.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Project Overview
&lt;/h3&gt;

&lt;p&gt;The challenge required building a system with the following core features:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Web Dashboard Features&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;A simple content creation interface using &lt;strong&gt;Fabric.js&lt;/strong&gt; to add text, images, and basic layouts.&lt;/li&gt;
&lt;li&gt;Content preview functionality.&lt;/li&gt;
&lt;li&gt;Optional content scheduling capabilities.&lt;/li&gt;
&lt;li&gt;A content list with status indicators (e.g., &lt;em&gt;draft&lt;/em&gt;, &lt;em&gt;posted&lt;/em&gt;).&lt;/li&gt;
&lt;li&gt;Basic content organization using folders or categories.&lt;/li&gt;
&lt;li&gt;Device status monitoring for the connected display app.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Electron Display Application&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Full-screen content display with support for text and images.&lt;/li&gt;
&lt;li&gt;Manual sync button with a status indicator.&lt;/li&gt;
&lt;li&gt;An auto-sync toggle option for seamless updates.&lt;/li&gt;
&lt;li&gt;Offline content playback for reliability.&lt;/li&gt;
&lt;li&gt;System tray controls for managing the app.&lt;/li&gt;
&lt;li&gt;A connection status indicator for the WebSocket server.&lt;/li&gt;
&lt;li&gt;Basic error notifications.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Offline Capabilities&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Local content storage for offline playback.&lt;/li&gt;
&lt;li&gt;Content caching to ensure smooth operation without an internet connection.&lt;/li&gt;
&lt;li&gt;Automatic content synchronization when the connection is restored.&lt;/li&gt;
&lt;li&gt;Offline status indicators for visibility.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Tackling the Implementation
&lt;/h3&gt;

&lt;p&gt;The first major challenge I faced was &lt;strong&gt;designing the implementation&lt;/strong&gt;. I restarted the project multiple times, initially focusing on the &lt;strong&gt;Electron app&lt;/strong&gt;. However, after much research and experimentation, I realized that starting with the &lt;strong&gt;web app&lt;/strong&gt; and then connecting it to the Electron display app was a better choice. This allowed me to establish the core functionality of content management before tackling the communication layer with WebSockets.&lt;/p&gt;

&lt;p&gt;For this project I chose Next.js due to its better integration with Vercel, making it incredibly easy to deploy and scale the app.&lt;/p&gt;




&lt;h3&gt;
  
  
  Learning Fabric.js and Electron from Scratch
&lt;/h3&gt;

&lt;p&gt;One of the biggest hurdles was learning &lt;strong&gt;Fabric.js&lt;/strong&gt; and &lt;strong&gt;Electron&lt;/strong&gt;, two tools I had never used before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fabric.js&lt;/strong&gt;: This powerful canvas library enabled the creation of interactive content like text, images, and shapes. However, its documentation was not very beginner-friendly, so I spent significant time watching tutorials and experimenting with examples.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Electron&lt;/strong&gt;: I appreciated Electron's well-documented "Quick Start" guide, which allowed me to quickly build the desktop application. It helped me understand how to wrap a web app into a desktop environment and implement offline features.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The WebSocket Challenge
&lt;/h3&gt;

&lt;p&gt;The WebSocket feature was particularly tricky. I successfully built a &lt;strong&gt;WebSocket server&lt;/strong&gt; that enabled the Electron app to replicate the web app's content. However, real-time updates between the two apps proved to be a challenge. While I couldn't get it working perfectly for this version, I plan to revisit and improve it to achieve full real-time synchronization.&lt;/p&gt;




&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;p&gt;This project was both challenging and rewarding. I gained valuable experience in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building &lt;strong&gt;web dashboards&lt;/strong&gt; with complex canvas interactions using Fabric.js.&lt;/li&gt;
&lt;li&gt;Creating a &lt;strong&gt;cross-platform desktop app&lt;/strong&gt; with Electron.&lt;/li&gt;
&lt;li&gt;Implementing offline-first features and handling content synchronization.&lt;/li&gt;
&lt;li&gt;Using &lt;strong&gt;WebSockets&lt;/strong&gt; for communication between a web app and a desktop app.&lt;/li&gt;
&lt;li&gt;Deploying modern applications using Next.js and Vercel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While I submitted the project, I’m excited to improve it further, add the missing features, and optimize the real-time capabilities.&lt;/p&gt;




&lt;h3&gt;
  
  
  Check Out the Project
&lt;/h3&gt;

&lt;p&gt;If you're curious, here’s where you can see the project live or contribute to its development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deployed Web App&lt;/strong&gt;: &lt;a href="https://signage-content-web-app.vercel.app/" rel="noopener noreferrer"&gt;https://signage-content-web-app.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GitHub Repository&lt;/strong&gt;: &lt;a href="https://github.com/HTSagara/signage-content-app" rel="noopener noreferrer"&gt;https://github.com/HTSagara/signage-content-app&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I’m making this project &lt;strong&gt;open-source&lt;/strong&gt; and would love for developers to collaborate, explore, and enhance it. Whether you're interested in &lt;strong&gt;improving the real-time features&lt;/strong&gt;, &lt;strong&gt;optimizing the Electron app&lt;/strong&gt;, or &lt;strong&gt;adding new functionality&lt;/strong&gt;, your contributions are welcome!&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;This project challenged me to step out of my comfort zone and explore new tools, frameworks, and design approaches. It reminded me that development is an iterative process filled with learning opportunities. I look forward to continuing this project, improving it over time, and sharing updates along the way. 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Open-Source Collaboration Progress</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Fri, 06 Dec 2024 19:16:46 +0000</pubDate>
      <link>https://dev.to/htsagara/open-source-collaboration-progress-10hj</link>
      <guid>https://dev.to/htsagara/open-source-collaboration-progress-10hj</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;Recently, I faced an interesting challenge while working on a project integrating Slack Bolt with Sanic - a framework I was previously unfamiliar with, which led to some unexpected deprecation warnings and type-related issues. I'll walk you through how I tackled the issue, the lessons I learned, and the precise code changes that resolved the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Sanic and Slack Bolt?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sanic
&lt;/h3&gt;

&lt;p&gt;Sanic is a high-performance, asynchronous web framework in Python. Designed to be fast, it takes advantage of Python's asyncio capabilities to handle large volumes of requests efficiently. Its minimalistic design makes it suitable for lightweight web applications, microservices, and API layers.&lt;/p&gt;

&lt;h3&gt;
  
  
  Slack Bolt
&lt;/h3&gt;

&lt;p&gt;Slack Bolt is a framework for building Slack apps. It abstracts the complexities of Slack's APIs, allowing developers to focus on creating interactive and event-driven Slack applications. With Bolt, you can manage commands, shortcuts, events, and more with ease.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;While implementing the integration, I encountered several warnings related to Sanic's cookie handling when running tests and handling requests. Here's an example of the warnings I saw:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DeprecationWarning: [DEPRECATION] Setting cookie values using the dict pattern has been deprecated.
DeprecationWarning: [DEPRECATION] Accessing cookies from the CookieJar by dict key is deprecated.
TypeError: Argument "path" to "add_cookie" of "BaseHTTPResponse" has incompatible type "Optional[Any]"; expected "str"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The root cause was the use of Sanic's old dict-based cookie handling syntax, which is no longer recommended as of Sanic v23.3. Instead, the new &lt;code&gt;add_cookie&lt;/code&gt; method must be used to ensure compatibility and eliminate these warnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;The key change was replacing the dict-based cookie handling with the &lt;code&gt;add_cookie&lt;/code&gt; method, ensuring that all cookie parameters passed were of the correct type.&lt;/p&gt;

&lt;p&gt;Here’s the updated code snippet:&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="c1"&gt;# Iterate over cookies and add them using Sanic's add_cookie method
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;cookie&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bolt_resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookies&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;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;cookie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="c1"&gt;# Convert "expires" field if provided
&lt;/span&gt;        &lt;span class="n"&gt;expire_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;expires&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;expires&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;expire_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;%a, %d %b %Y %H:%M:%S %Z&lt;/span&gt;&lt;span class="sh"&gt;"&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;expire_value&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;

        &lt;span class="c1"&gt;# Convert "max-age" if provided
&lt;/span&gt;        &lt;span class="n"&gt;max_age&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max-age&lt;/span&gt;&lt;span class="sh"&gt;"&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max-age&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="bp"&gt;None&lt;/span&gt;

        &lt;span class="c1"&gt;# Ensure values are of the correct type before passing to add_cookie
&lt;/span&gt;        &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="n"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain&lt;/span&gt;&lt;span class="sh"&gt;"&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;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;domain&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="bp"&gt;None&lt;/span&gt;

        &lt;span class="c1"&gt;# Add cookie with Sanic's add_cookie method
&lt;/span&gt;        &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_cookie&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;expires&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_age&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;max_age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;secure&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;httponly&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&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;Replaced Dict-Based Syntax:&lt;/strong&gt; The old approach relied on direct manipulation of &lt;code&gt;resp.cookies&lt;/code&gt; using dict syntax, which is deprecated. Instead, we used &lt;code&gt;resp.add_cookie()&lt;/code&gt; to set cookies in a forward-compatible way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ensured Proper Data Types:&lt;/strong&gt; Parameters like path and domain were sometimes &lt;code&gt;None&lt;/code&gt; or not strings. We explicitly converted these values to &lt;code&gt;strings&lt;/code&gt; or set defaults ("/" for path, None for domain) before passing them to add_cookie.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Handled Optional Cookie Fields:&lt;/strong&gt; expires was parsed into a &lt;code&gt;datetime&lt;/code&gt; object if provided, using the format &lt;code&gt;"%a, %d %b %Y %H:%M:%S %Z"&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;max-age&lt;/code&gt; was converted to an integer if available.&lt;/p&gt;

&lt;p&gt;These changes resolved all warnings and errors, ensuring the integration adhered to Sanic's modern practices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Since I had no prior experience with Sanic, understanding its documentation was critical. Learning how Sanic handles cookies and requests helped me realize why the old syntax was problematic and how the new &lt;code&gt;add_cookie&lt;/code&gt; method works.&lt;/p&gt;

&lt;p&gt;Integrating Slack Bolt with Sanic turned out to be a rewarding challenge. Not only did it improve my understanding of Sanic, but it also emphasized the importance of staying up-to-date with framework best practices. If you're facing similar issues, I hope this blog post provides clarity and helps you solve your problem more efficiently.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>slackbolt</category>
      <category>slack</category>
      <category>python</category>
    </item>
    <item>
      <title>Continue contributing to Open-Source projects</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Mon, 25 Nov 2024 14:55:08 +0000</pubDate>
      <link>https://dev.to/htsagara/continue-contributing-to-open-source-projects-3460</link>
      <guid>https://dev.to/htsagara/continue-contributing-to-open-source-projects-3460</guid>
      <description>&lt;p&gt;If you read my blogs, you may know that I've been trying to gain experience by contributing to Open-Source projects.&lt;/p&gt;

&lt;p&gt;Lately, I've been contributing to &lt;strong&gt;python slack-sdk&lt;/strong&gt;. If you haven't seen my blog posts yet, you can check them out here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-2782"&gt;Blog post 1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-part-2-366f"&gt;Blog post 2&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Updates about slack-sdk issue
&lt;/h2&gt;

&lt;p&gt;Just an update about this issue: my latest PR hasn't been merged yet. One of the maintainers wrote additional test cases to ensure that the URL-related changes don't introduce any failures. I'm currently waiting for their tests to be merged so I can re-test the code and make any necessary updates.&lt;/p&gt;

&lt;p&gt;This has been a valuable learning experience, as it has shown me how collaborative problem-solving works in large-scale projects. The additional test cases highlight the importance of robust testing practices in Open-Source development, ensuring the stability and reliability of the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;I'm planning to continue contributing to &lt;strong&gt;Slack&lt;/strong&gt;, whether in the same project or others. My immediate priority is to get that previous PR merged. In the meantime, I'm exploring other issues to work on.&lt;/p&gt;

&lt;p&gt;I recently discovered a large and exciting project from &lt;strong&gt;Slack&lt;/strong&gt; called &lt;strong&gt;Nebula&lt;/strong&gt;. For those unfamiliar, Nebula is Slack’s open-source secure global overlay network, designed to simplify connectivity between devices, even across different networks. It’s widely recognized for its reliability and innovation in creating seamless connectivity. Nebula enables devices to communicate securely as if they were on the same physical network, regardless of their actual location. However, this project requires a solid understanding of &lt;strong&gt;Golang&lt;/strong&gt;, so I'll need to invest time in learning it before tackling any contributions there.&lt;/p&gt;

&lt;p&gt;To prepare, I've started exploring Golang tutorials and small projects to build my confidence and understanding of the language. If you have any recommendations for learning Golang or working on similar projects, feel free to share them with me!&lt;/p&gt;

&lt;p&gt;I've also come across other issues involving Python and TypeScript that I can begin exploring. These issues provide a great opportunity to strengthen my skills and continue building my portfolio of contributions.&lt;/p&gt;

&lt;p&gt;Additionally, I'm open to contributing to Open-Source projects from other companies as well. I believe diversifying my contributions will help me grow as a developer and expose me to new tools and methodologies.&lt;/p&gt;

&lt;p&gt;I'll continue to share my experiences, lessons learned, and progress along the way.&lt;/p&gt;

</description>
      <category>opensource</category>
    </item>
    <item>
      <title>ReadmeGenie is Ready for You! Automating Releases with GitHub Actions</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Fri, 22 Nov 2024 21:19:35 +0000</pubDate>
      <link>https://dev.to/htsagara/readmegenie-is-ready-for-you-automating-releases-with-github-actions-4fmn</link>
      <guid>https://dev.to/htsagara/readmegenie-is-ready-for-you-automating-releases-with-github-actions-4fmn</guid>
      <description>&lt;p&gt;I'm thrilled to announce the &lt;strong&gt;first release&lt;/strong&gt; of &lt;a href="https://test.pypi.org/project/ReadmeGenie/" rel="noopener noreferrer"&gt;ReadmeGenie&lt;/a&gt;! 🎉 This project has been incredibly rewarding to see it come to life on PyPI. With version &lt;strong&gt;1.0.0&lt;/strong&gt;, ReadmeGenie is now available for developers everywhere to generate clean, concise, and professional &lt;code&gt;README.md&lt;/code&gt; files for their projects effortlessly.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;What is ReadmeGenie?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;ReadmeGenie is a Python-based CLI tool designed to simplify the creation of &lt;code&gt;README.md&lt;/code&gt; files. By automating the tedious process of writing detailed documentation, ReadmeGenie helps developers focus more on coding and less on formatting.&lt;/p&gt;

&lt;p&gt;With &lt;strong&gt;ReadmeGenie&lt;/strong&gt;, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generate structured &lt;code&gt;README.md&lt;/code&gt; files with sections like &lt;strong&gt;Installation&lt;/strong&gt;, &lt;strong&gt;Usage&lt;/strong&gt;, and &lt;strong&gt;Contributing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Incorporate API integration to tailor your README based on your project type.&lt;/li&gt;
&lt;li&gt;Quickly update your README files as your project evolves.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ReadmeGenie is a game-changer for developers working on open-source projects or collaborative repositories. If you want to give it a try, you can install it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; https://test.pypi.org/simple/ &lt;span class="nv"&gt;ReadmeGenie&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check out the &lt;a href="https://github.com/HTSagara/readme_genie/" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; to learn more, explore the codebase, or contribute to the project.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Automating Releases with GitHub Actions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One of the highlights of ReadmeGenie’s journey is how we’ve automated its deployment process to PyPI using &lt;strong&gt;GitHub Actions&lt;/strong&gt;. Every time a new version tag is pushed to the repository, our automation pipeline builds the package, runs tests, and publishes it to PyPI. Here’s how we achieved this:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Version Management with Git Tags&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We integrated &lt;code&gt;setuptools_scm&lt;/code&gt; to fetch the project version directly from Git tags. This ensures that every release is versioned correctly without needing manual updates to the &lt;code&gt;pyproject.toml&lt;/code&gt; file. By tagging a release (e.g., &lt;code&gt;v1.0.0&lt;/code&gt;), the pipeline automatically sets the version dynamically.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Automated Workflows with GitHub Actions&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Our GitHub Actions workflow includes the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Testing and Linting:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every push triggers tests using &lt;code&gt;pytest&lt;/code&gt; and code linting with &lt;code&gt;flake8&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;This ensures the project remains robust and adheres to Python best practices.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Building the Package:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The pipeline builds the distribution files (&lt;code&gt;sdist&lt;/code&gt; and &lt;code&gt;wheel&lt;/code&gt;) using &lt;code&gt;setuptools&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Publishing to PyPI:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;With the help of &lt;code&gt;twine&lt;/code&gt;, the built packages are uploaded to PyPI or TestPyPI, depending on the environment.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s a snippet from our GitHub Actions workflow:&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish to PyPI&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;  &lt;span class="c1"&gt;# Trigger workflow for version tags&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build-and-publish&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.12"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python3 -m pip install --upgrade pip build setuptools_scm twine&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build package&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3 -m build&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Publish package to PyPI&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;TWINE_USERNAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;__token__&lt;/span&gt;
          &lt;span class="na"&gt;TWINE_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.PYPI_API_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python3 -m twine upload dist/*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;3. Secrets Management&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;To ensure security, the PyPI API token is stored as a &lt;strong&gt;GitHub secret&lt;/strong&gt; (&lt;code&gt;PYPI_API_TOKEN&lt;/code&gt;) and injected into the workflow at runtime. This eliminates the need to expose sensitive information in the codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;What’s Next for ReadmeGenie?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;This is just the beginning! 🚀 For future releases, we plan to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add support for more customizable templates.&lt;/li&gt;
&lt;li&gt;Integrate advanced NLP tools to generate contextual README sections.&lt;/li&gt;
&lt;li&gt;Suppor more GenAI tools other than Groq and Cohere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re also looking forward to collaborating with the community to make ReadmeGenie even better. Feel free to contribute or report issues on our &lt;a href="https://github.com/HTSagara/readme_genie/" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;A Final Word&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The journey to deploying ReadmeGenie wasn’t without its challenges, but automating the release process with GitHub Actions has been a game-changer. It ensures that every release is seamless, consistent, and reliable.&lt;/p&gt;

&lt;p&gt;If you’re a developer who finds writing documentation tedious or repetitive, give ReadmeGenie a try. We can’t wait to see the amazing projects you’ll create with it!&lt;/p&gt;

&lt;p&gt;Happy coding! 😊&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>automation</category>
      <category>genai</category>
    </item>
    <item>
      <title>Collaborating to Slack as an Open-Source Developer: Part 2</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Wed, 20 Nov 2024 19:32:21 +0000</pubDate>
      <link>https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-part-2-366f</link>
      <guid>https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-part-2-366f</guid>
      <description>&lt;h2&gt;
  
  
  Recap of Part 1
&lt;/h2&gt;

&lt;p&gt;In my &lt;a href="https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-2782"&gt;first blog post&lt;/a&gt;, I shared my journey of contributing to the Slack SDK as an open-source developer. I tackled an issue related to ensuring the base URL for API requests had a trailing slash to simplify URL construction and prevent inconsistencies. If you haven’t read it yet, I recommend starting there to get the context for this follow-up.&lt;/p&gt;




&lt;h2&gt;
  
  
  A New Challenge Arises
&lt;/h2&gt;

&lt;p&gt;After completing my first contribution, I was eager to tackle another issue in the same project. As I prepared to start, I noticed a problem in one of the authentication tests. The issue stemmed from the trailing slash feature I had previously implemented.&lt;/p&gt;

&lt;p&gt;Here’s what happened: the &lt;code&gt;base_url&lt;/code&gt; now always had a trailing slash appended during initialization. However, the &lt;code&gt;api_method&lt;/code&gt; used in some test cases also started with a &lt;code&gt;/&lt;/code&gt;. This combination caused a &lt;strong&gt;double slash&lt;/strong&gt; (e.g., &lt;code&gt;https://slack.com/api//auth.test&lt;/code&gt;), which broke some of the API requests.&lt;/p&gt;




&lt;h2&gt;
  
  
  Reporting the Issue
&lt;/h2&gt;

&lt;p&gt;Realizing the significance of this bug, I quickly reported it to the maintainers and opened a new issue describing the problem. To ensure transparency and provide a clear solution path, I also submitted a pull request addressing the bug. However, the maintainers decided to revert my original merge to prevent disruptions in the main branch and asked me to submit a new PR with the necessary fixes and tests for edge cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Fix and New Implementation
&lt;/h2&gt;

&lt;p&gt;To address the problem, I reworked the &lt;code&gt;_get_url&lt;/code&gt; function and added additional safeguards to prevent double slashes, even when both &lt;code&gt;base_url&lt;/code&gt; and &lt;code&gt;api_method&lt;/code&gt; contained trailing or leading slashes.&lt;/p&gt;

&lt;p&gt;Here’s the updated implementation:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_get_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Joins the base Slack URL and an API method to form an absolute URL.

    Args:
        base_url (str): The base URL (always ends with &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="s"&gt;).
        api_method (str): The Slack Web API method. e.g., &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chat.postMessage&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.

    Returns:
        str: The absolute API URL, e.g., &lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/chat.postMessage&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="c1"&gt;# Strip leading slash from api_method to prevent double slashes
&lt;/span&gt;    &lt;span class="n"&gt;api_method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api_method&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lstrip&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="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;urljoin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;api_method&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Key adjustments
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Strip Leading Slash: By using &lt;code&gt;.lstrip("/")&lt;/code&gt; on &lt;code&gt;api_method&lt;/code&gt;, the function ensures no double slashes occur during concatenation.&lt;/li&gt;
&lt;li&gt;Test Case Enhancements: I expanded the test suite to cover scenarios such as:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;base_url&lt;/code&gt; with and without a trailing slash.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;api_method&lt;/code&gt; with and without a leading slash.
Edge cases where both had slashes.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s an example of the updated test:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_get_url_prevent_double_slash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/&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;/auth.test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/auth.test&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;Should prevent double slashes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api&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;auth.test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/auth.test&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;Should handle base_url without trailing slash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/&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;auth.test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/auth.test&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;Should handle api_method without leading slash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;api_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api&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;/auth.test&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://slack.com/api/auth.test&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;Should handle both inputs cleanly&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;h2&gt;
  
  
  Reflections on Testing and Edge Cases
&lt;/h2&gt;

&lt;p&gt;This experience taught me the importance of thorough testing. Even though my original implementation passed all existing tests, it didn’t account for certain edge cases, such as leading slashes in api_method.&lt;/p&gt;

&lt;p&gt;Here are my key takeaways:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Unit Tests Aren’t Foolproof:&lt;/strong&gt; While unit tests help catch many issues, they might not cover all edge cases. A feature can still have loose ends, especially if the inputs vary widely.&lt;br&gt;
   &lt;strong&gt;2. Collaborate and Communicate:&lt;/strong&gt; Reporting bugs promptly and discussing solutions with maintainers can prevent larger disruptions. Their decision to revert my changes emphasized the importance of keeping the main branch stable.&lt;br&gt;
   &lt;strong&gt;3. Iterate and Learn:&lt;/strong&gt; Open-source contributions are iterative. Each step is an opportunity to improve, learn from feedback, and strengthen your coding practices.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Contributing to Slack’s SDK has been an invaluable experience. This journey, from implementing a new feature to resolving its unintended side effects, highlighted the complexities of real-world software development and the collaborative spirit of open source.&lt;/p&gt;

&lt;p&gt;If you’re considering contributing to an open-source project, don’t let the fear of making mistakes hold you back. Every bug, every fix, and every test written is a step toward becoming a better developer.&lt;/p&gt;

&lt;p&gt;What challenges have you faced in your open-source contributions? Let’s discuss in the comments below!&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>slack</category>
      <category>slackapi</category>
    </item>
    <item>
      <title>Collaborating to Slack as an Open-Source Developer</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Tue, 19 Nov 2024 14:26:54 +0000</pubDate>
      <link>https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-2782</link>
      <guid>https://dev.to/htsagara/collaborating-to-slack-as-an-open-source-developer-2782</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Are you a beginner developer looking to gain more experience and wondering where to start? This blog post is for you!&lt;/p&gt;

&lt;p&gt;One of the best ways to build your skills is by contributing to open-source projects. Recently, I tackled an issue in the &lt;a href="https://github.com/slackapi" rel="noopener noreferrer"&gt;SlackAPI GitHub Repo&lt;/a&gt;. At the time of writing, my pull request (PR) has been accepted by one of the maintainers, it has passed all the CI builds and just got merged!🙌&lt;/p&gt;

&lt;p&gt;In this post, I’ll share my journey fixing this issue, the challenges I faced, and the lessons I learned along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Issue
&lt;/h2&gt;

&lt;p&gt;The issue I worked on was about handling URLs in the Slack API. Specifically, the code was not appending a trailing slash (&lt;code&gt;/&lt;/code&gt;) to the &lt;code&gt;base_url&lt;/code&gt; if it was missing, which could lead to inconsistent or failed API calls.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Without the trailing slash: &lt;code&gt;https://slack.com/api&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;With the trailing slash: &lt;code&gt;https://slack.com/api/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The missing trailing slash caused subtle issues that needed to be addressed to ensure reliability. My goal was to modify the code to automatically append the slash if it wasn’t provided.&lt;/p&gt;

&lt;p&gt;You can find the original issue here: &lt;a href="https://github.com/slackapi/python-slack-sdk/issues/1541" rel="noopener noreferrer"&gt;#1541&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Finding the Right Files to Edit
&lt;/h3&gt;

&lt;p&gt;As a beginner tackling a large codebase, my first challenge was finding where to make the changes. Navigating through unfamiliar directories and understanding the project’s structure took time. Reading the documentation and stepping through the code helped me locate the relevant files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the Fix
&lt;/h3&gt;

&lt;p&gt;Once I identified the file (&lt;code&gt;base_client.py&lt;/code&gt;), I drafted a solution to ensure the &lt;code&gt;base_url&lt;/code&gt; ended with a &lt;code&gt;/&lt;/code&gt;. However, before implementing it, I reached out to the maintainers for guidance. They pointed out that similar logic should also be applied to two other files: &lt;code&gt;async_base_client.py&lt;/code&gt; and &lt;code&gt;legacy_base_client.py&lt;/code&gt;. This was a great reminder of the importance of clear communication and pre-discussion in collaborative projects.&lt;/p&gt;

&lt;p&gt;Here’s the updated code snippet for appending the trailing slash:&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="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&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="n"&gt;base_url&lt;/span&gt; &lt;span class="o"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple change ensured that all API calls would use a consistent base URL. I replicated this logic in the other two files as well, maintaining consistency across the different clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Tests
&lt;/h2&gt;

&lt;p&gt;Testing was a critical part of this fix. I added new tests in the &lt;code&gt;test_web_client.py&lt;/code&gt; file to verify that the &lt;code&gt;base_url&lt;/code&gt; was corrected if it was missing the trailing slash. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Input: &lt;a href="http://localhost:8888" rel="noopener noreferrer"&gt;http://localhost:8888&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Expected output: &lt;a href="http://localhost:8888/" rel="noopener noreferrer"&gt;http://localhost:8888/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a simplified version of one of the tests I added:&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_base_url_appends_trailing_slash_issue_15141&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8888/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_base_url_preserves_trailing_slash_issue_15141&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WebClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8888/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;base_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8888/&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;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Importance of Testing
&lt;/h3&gt;

&lt;p&gt;Before submitting your PR, test your changes thoroughly. Writing unit tests, running existing tests, and using debugging tools are crucial steps to ensure your code works as expected. By automating tests for edge cases, you can catch potential errors early and increase confidence in your solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Follow the Contribution Guidelines
&lt;/h3&gt;

&lt;p&gt;Every open-source project has its own contribution guidelines. Make sure to read and follow them carefully, as they outline coding standards, testing requirements, and PR submission processes. For example, SlackAPI provided clear instructions on setting up the development environment and running tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Is Key
&lt;/h3&gt;

&lt;p&gt;Pre-discussing your changes with maintainers can save time and prevent unnecessary back-and-forth. In my case, it ensured I applied the fix to all relevant files, making the solution more comprehensive.&lt;br&gt;
Don’t Be Afraid to Ask for Help&lt;/p&gt;

&lt;p&gt;If you’re stuck, don’t hesitate to ask questions. Open-source maintainers and contributors are often supportive and willing to help newcomers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Contributing to open source can feel intimidating at first, but it’s one of the best ways to grow as a developer. You’ll learn to work in a team, navigate large codebases, and write production-ready code. Plus, it feels amazing to see your work being used by others!&lt;/p&gt;

&lt;p&gt;If you’re a beginner developer, I encourage you to explore open-source projects, find an issue that interests you, and take the plunge. The &lt;a href="https://github.com/slackapi" rel="noopener noreferrer"&gt;SlackAPI GitHub Repo&lt;/a&gt; is a great place to start!&lt;/p&gt;

&lt;p&gt;Have you contributed to an open-source project? Share your experience in the comments below! 🚀&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>slack</category>
      <category>slackapi</category>
    </item>
    <item>
      <title>Implementing CI/CD for ReadmeGenie</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Mon, 11 Nov 2024 23:30:23 +0000</pubDate>
      <link>https://dev.to/htsagara/implementing-cicd-for-readmegenie-5d1n</link>
      <guid>https://dev.to/htsagara/implementing-cicd-for-readmegenie-5d1n</guid>
      <description>&lt;h2&gt;
  
  
  Why CI/CD?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the setup, let’s briefly cover why CI/CD is so critical:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Automated Testing&lt;/strong&gt;: Running tests automatically ensures code is stable with every change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: CI/CD enforces standards (linting, formatting) across the codebase.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reliability&lt;/strong&gt;: Automated checks and tests minimize human error and improve code reliability.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapid Feedback&lt;/strong&gt;: Developers receive immediate feedback on code quality, allowing issues to be caught early.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In &lt;a href="https://github.com/HTSagara/readme_genie/" rel="noopener noreferrer"&gt;&lt;strong&gt;ReadmeGenie&lt;/strong&gt;&lt;/a&gt;, we leveraged GitHub Actions as our CI/CD tool. It integrates smoothly with GitHub repositories and offers flexibility and automation through YAML configuration files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The CI/CD Pipeline for ReadmeGenie
&lt;/h2&gt;

&lt;p&gt;Our CI/CD pipeline includes the following automated steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Linting and Formatting Checks&lt;/strong&gt;: We run &lt;strong&gt;Ruff&lt;/strong&gt; and &lt;strong&gt;Black&lt;/strong&gt; to ensure code style and consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unit Testing&lt;/strong&gt;: We use &lt;code&gt;unittest&lt;/code&gt; to verify that code changes don’t break existing functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Coverage Analysis&lt;/strong&gt;: We use &lt;strong&gt;Coverage.py&lt;/strong&gt; to ensure that the code meets our coverage threshold before a commit is allowed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pre-Commit Hooks&lt;/strong&gt;: We added hooks to enforce local quality checks before pushing changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Overview of the GitHub Actions Workflow
&lt;/h3&gt;

&lt;p&gt;The CI workflow is defined in &lt;code&gt;.github/workflows/python-app.yml&lt;/code&gt;. Here’s a breakdown of what each part of the workflow does:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Triggering the Workflow
&lt;/h3&gt;

&lt;p&gt;The workflow runs on every push and pull request to the &lt;code&gt;main&lt;/code&gt; branch. This ensures that all code changes undergo validation before merging into production.&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Python application&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;main"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Setting Up the Python Environment
&lt;/h3&gt;

&lt;p&gt;We configure GitHub Actions to use Python 3.12.x, ensuring consistency with our local development environment. This step installs the specific Python version and prepares the environment for dependency installation.&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;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Set up Python 3.12.x&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;python-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3.12.x"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Installing Dependencies
&lt;/h3&gt;

&lt;p&gt;The next step is to install project dependencies. Here, we upgrade &lt;code&gt;pip&lt;/code&gt; and install &lt;code&gt;requirements.txt&lt;/code&gt; file, it will install additional dependencies specified there.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install dependencies&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;python -m pip install --upgrade pip&lt;/span&gt;
          &lt;span class="s"&gt;pip install flake8 pytest&lt;/span&gt;
          &lt;span class="s"&gt;if [ -f requirements.txt ]; then pip install -r requirements.txt; fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Running Linting and Code Quality Checks
&lt;/h3&gt;

&lt;p&gt;Linting is a crucial part of our workflow, ensuring the code adheres to specified quality standards. We run &lt;strong&gt;flake8&lt;/strong&gt; with options to flag syntax errors, undefined names, and complexity issues.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint with flake8&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;# stop the build if there are Python syntax errors or undefined names&lt;/span&gt;
          &lt;span class="s"&gt;flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics&lt;/span&gt;
          &lt;span class="s"&gt;# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide&lt;/span&gt;
          &lt;span class="s"&gt;flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Running Tests with Coverage Analysis
&lt;/h3&gt;

&lt;p&gt;For unit tests, we use &lt;code&gt;pytest&lt;/code&gt; to run all test cases. Additionally, we use &lt;code&gt;coverage&lt;/code&gt; to track which lines of code are tested, ensuring that our test suite meets the defined coverage threshold of 75%. &lt;/p&gt;

&lt;p&gt;The following commands run the tests and generate a coverage report, highlighting any gaps in test coverage. This is essential for quality assurance, as untested code is a potential source of future bugs.&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test with pytest&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;pytest&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Test functions and coverage&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;coverage run --source=. -m unittest discover -s tests&lt;/span&gt;
          &lt;span class="s"&gt;coverage report -m --fail-under=75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This coverage check ensures a high standard of code quality by enforcing that at least 75% of the codebase is covered by tests. If coverage falls below this threshold, the commit will not be allowed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating Pre-Commit Hooks
&lt;/h2&gt;

&lt;p&gt;In addition to CI/CD, we set up &lt;strong&gt;pre-commit hooks&lt;/strong&gt; to enforce code quality locally before any changes are pushed to the repository. These hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run Ruff for linting and Black for formatting.&lt;/li&gt;
&lt;li&gt;Enforce a minimum coverage threshold by running the tests with coverage locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s how we added the coverage check as a pre-commit hook in &lt;code&gt;.pre-commit-config.yaml&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;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-coverage&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Coverage&lt;/span&gt;
        &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash -c "coverage run --source=. -m unittest discover -s tests &amp;amp;&amp;amp; coverage report -m --fail-under=75"&lt;/span&gt;
        &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Challenges and Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Setting up CI/CD required a deep understanding of how different tools (flake8, pytest, coverage) interact within GitHub Actions. Here are some challenges we faced and the solutions we implemented:&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Divergent Local and Remote Configurations
&lt;/h3&gt;

&lt;p&gt;We encountered issues with environment variable conflicts, especially in testing API integration and configuration handling. Using &lt;code&gt;@patch.dict&lt;/code&gt; and other mocking techniques in &lt;code&gt;unittest&lt;/code&gt; allowed us to simulate the environment effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Coverage and Thresholds
&lt;/h3&gt;

&lt;p&gt;The biggest challenge was ensuring adequate test coverage. Using &lt;code&gt;coverage.py&lt;/code&gt; with &lt;code&gt;--fail-under=75&lt;/code&gt; in both GitHub Actions and pre-commit hooks helped enforce this standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Improvements
&lt;/h2&gt;

&lt;p&gt;To make the CI/CD pipeline more robust, we plan to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add Deployment Stages&lt;/strong&gt;: Automate deployment to a staging or production environment after tests pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automate Code Quality Badges&lt;/strong&gt;: Add dynamic badges to display coverage, linting status, and test results in the README.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expand Coverage Requirements&lt;/strong&gt;: Increase the coverage threshold as we improve tests and cover more edge cases.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Through this project, I realized the importance of establishing robust testing and CI/CD practices early on. If I were to start again, I’d focus on writing comprehensive tests from the beginning and incrementally expanding and improving them as the project progresses. This approach would prevent missing branches or untested areas and ensure that all new code integrates smoothly into a well-covered codebase.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>cicd</category>
      <category>testing</category>
      <category>python</category>
    </item>
    <item>
      <title>Implementing Unit Testing in ReadmeGenie</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Thu, 07 Nov 2024 19:23:57 +0000</pubDate>
      <link>https://dev.to/htsagara/implementing-unit-testing-in-readmegenie-1jcj</link>
      <guid>https://dev.to/htsagara/implementing-unit-testing-in-readmegenie-1jcj</guid>
      <description>&lt;p&gt;In this post, I’ll walk through the journey of implementing unit testing, handling complex configuration challenges, and introducing robust code coverage in &lt;a href="https://github.com/HTSagara/readme_genie" rel="noopener noreferrer"&gt;ReadmeGenie&lt;/a&gt;. From initial test design to setting up pre-commit hooks, this process involved a range of improvements in code quality, reliability, and developer workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Setting Up the Testing Environment
&lt;/h3&gt;

&lt;p&gt;To start, I chose &lt;code&gt;unittest&lt;/code&gt; as the primary framework for writing and executing tests. Python’s built-in &lt;code&gt;unittest&lt;/code&gt; provides a structured approach for defining test cases, and its integration with &lt;code&gt;mock&lt;/code&gt; makes it ideal for testing complex configurations and API calls.&lt;/p&gt;

&lt;p&gt;I created a dedicated test runner (&lt;code&gt;tests/test_runner.py&lt;/code&gt;) for automatic discovery and execution of all test files in the &lt;code&gt;tests/&lt;/code&gt; directory:&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="c1"&gt;# tests/test_runner.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;loader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TestLoader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;suite&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;discover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;start_dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tests&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;test_*.py&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;runner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TextTestRunner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;verbosity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;runner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;suite&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This setup ensures that running &lt;code&gt;python tests/test_runner.py&lt;/code&gt; will automatically load and run all test files, making it easy to validate the project’s overall functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Structuring the Unit Tests
&lt;/h3&gt;

&lt;p&gt;The ReadmeGenie project required comprehensive testing for several components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Argument Parsing&lt;/strong&gt;: Verifying correct parsing of command-line arguments and handling of default values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Configuration and Environment Handling&lt;/strong&gt;: Testing for proper retrieval of API keys and handling errors when they’re missing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API Calls&lt;/strong&gt;: Using mocks to simulate API requests to avoid real API calls in tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helper Functions&lt;/strong&gt;: Testing utility functions, such as file reading and README processing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each test file is named according to the module it tests (e.g., &lt;code&gt;test_parse_arg.py&lt;/code&gt; for argument parsing and &lt;code&gt;test_model.py&lt;/code&gt; for model functions), ensuring a clear, maintainable structure.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Biggest Challenge: Configuring &lt;code&gt;test_loadConfig.py&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Setting up &lt;code&gt;test_loadConfig.py&lt;/code&gt; turned out to be the most challenging part of this project. Initially, I encountered persistent issues related to environment variables and file path checks. Since &lt;code&gt;load_config()&lt;/code&gt; is intended to handle various configuration sources (e.g., environment variables, &lt;code&gt;.env&lt;/code&gt; files, JSON, and TOML files), the tests required extensive mocking to simulate these environments accurately.&lt;/p&gt;

&lt;h4&gt;
  
  
  Errors and Solutions in &lt;code&gt;test_loadConfig.py&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;The primary issues involved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Environment Variable Conflicts&lt;/strong&gt;: Existing environment variables sometimes interfered with mocked values. Using &lt;code&gt;@patch.dict("os.environ", {}, clear=True)&lt;/code&gt;, I cleared the environment variables within the test scope to ensure consistent results.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;File Path Checks&lt;/strong&gt;: Since &lt;code&gt;load_config()&lt;/code&gt; checks for file existence, I used &lt;code&gt;os.path.exists&lt;/code&gt; to simulate scenarios where configuration files were present or absent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mocking &lt;code&gt;open&lt;/code&gt; and &lt;code&gt;toml.load&lt;/code&gt;&lt;/strong&gt;: These required precise mocking to handle cases of missing, empty, or populated configuration files. Using &lt;code&gt;mock_open&lt;/code&gt; with &lt;code&gt;patch&lt;/code&gt; on &lt;code&gt;toml.load&lt;/code&gt;, I effectively simulated each situation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After resolving these issues, &lt;code&gt;test_loadConfig.py&lt;/code&gt; now covers three main scenarios:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Empty Configuration&lt;/strong&gt;: Tests that an empty configuration is returned when no environment variables or files are found.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Valid Configuration Data&lt;/strong&gt;: Tests that the &lt;code&gt;api_key&lt;/code&gt; is correctly retrieved from the configuration file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File Not Found&lt;/strong&gt;: Simulates a missing file, expecting an empty configuration to be returned.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here’s the final version of &lt;code&gt;test_loadConfig.py&lt;/code&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;unittest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;unittest.mock&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;mock_open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;patch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;loadConfig&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_config&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestLoadConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;unittest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@patch.dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os.environ&lt;/span&gt;&lt;span class="sh"&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;clear&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.os.getenv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.os.path.exists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;builtins.open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_callable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock_open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read_data&lt;/span&gt;&lt;span class="o"&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="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.toml.load&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_load_config_empty_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_toml_load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_open_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_exists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_getenv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;

    &lt;span class="nd"&gt;@patch.dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os.environ&lt;/span&gt;&lt;span class="sh"&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;clear&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.os.getenv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.os.path.exists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;builtins.open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_callable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mock_open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;read_data&lt;/span&gt;&lt;span class="o"&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="s"&gt;api_key&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="s"&gt;test_key&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="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.toml.load&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&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;api_key&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;test_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_load_config_with_valid_data&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_toml_load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_open_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_exists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_getenv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api_key&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;test_key&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@patch.dict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;os.environ&lt;/span&gt;&lt;span class="sh"&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;clear&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.os.getenv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.os.path.exists&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;builtins.open&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;side_effect&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;FileNotFoundError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@patch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;loadConfig.toml.load&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;return_value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{})&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_load_config_file_not_found&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_toml_load&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_open_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_exists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mock_getenv&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assertEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;config&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;
  
  
  4. Code Coverage Analysis
&lt;/h3&gt;

&lt;p&gt;With our tests in place, we focused on measuring and improving coverage using &lt;code&gt;coverage.py&lt;/code&gt;. By setting an 80% threshold, we aimed to ensure all critical parts of the code are tested.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tool Configuration for Coverage
&lt;/h4&gt;

&lt;p&gt;I configured &lt;code&gt;coverage.py&lt;/code&gt; with the following settings in &lt;code&gt;pyproject.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[tool.coverage.run]&lt;/span&gt;
&lt;span class="py"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;branch&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;omit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"tests/*"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[tool.coverage.report]&lt;/span&gt;
&lt;span class="py"&gt;show_missing&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;fail_under&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;75&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration includes branch coverage, highlights missing lines, and enforces a minimum 75% coverage threshold. &lt;/p&gt;

&lt;h4&gt;
  
  
  Pre-Commit Coverage Check
&lt;/h4&gt;

&lt;p&gt;To integrate this into the development workflow, I added a pre-commit hook to ensure code coverage is checked on each commit. If coverage falls below 75%, the commit is blocked, prompting developers to improve coverage before proceeding:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&lt;/span&gt;
  &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-coverage&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Coverage&lt;/span&gt;
      &lt;span class="na"&gt;entry&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash -c "coverage run --source=. -m unittest discover -s tests &amp;amp;&amp;amp; coverage report -m --fail-under=75"&lt;/span&gt;
      &lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;system&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Current Coverage and Opportunities for Improvement
&lt;/h3&gt;

&lt;p&gt;Our recent coverage report shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name                   Stmts   Miss Branch BrPart  Cover   Missing
------------------------------------------------------------------
loadConfig.py              8      0      2      0   100%
logging_config.py         19      0      2      0   100%
models/__init__.py         0      0      0      0   100%
models/cohere_api.py      12      0      0      0   100%
models/groq_api.py        12      0      0      0   100%
models/model.py           64     13     16      3    78%   25-26, 44-46, 70-74, 84-87
readme_genie.py           37     16      4      1    54%   20-24, 66-86, 90
------------------------------------------------------------------
TOTAL                    152     29     24      4    79%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While coverage is strong in some areas (e.g., &lt;code&gt;loadConfig.py&lt;/code&gt; at 100%), there are still opportunities for improvement in &lt;code&gt;models/model.py&lt;/code&gt; and &lt;code&gt;readme_genie.py&lt;/code&gt;. Focusing on untested branches and edge cases will be crucial to reaching our goal of 85% or higher overall coverage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;This project has taught me a lot about unit testing, mocking, and code coverage. Setting up &lt;code&gt;test_loadConfig.py&lt;/code&gt; was a particularly valuable experience, pushing me to explore deeper levels of configuration mocking. The pre-commit hook for coverage has added a layer of quality assurance, enforcing consistent test standards.&lt;/p&gt;

&lt;p&gt;Moving forward, I aim to refine these tests further by adding edge cases and improving branch coverage. This will not only make ReadmeGenie more robust but also lay a solid foundation for future development.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>testing</category>
      <category>python</category>
      <category>unittest</category>
    </item>
    <item>
      <title>Setting Up Tools for Code Quality</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Tue, 29 Oct 2024 20:34:53 +0000</pubDate>
      <link>https://dev.to/htsagara/setting-up-tools-for-code-quality-50fj</link>
      <guid>https://dev.to/htsagara/setting-up-tools-for-code-quality-50fj</guid>
      <description>&lt;p&gt;When developing &lt;strong&gt;ReadmeGenie&lt;/strong&gt;, I aimed to ensure consistent code quality with an automated setup for linting and formatting. After considering several tools, I selected Ruff as the linter and Black as the code formatter. Although Ruff can also handle both linting and formatting, I decided to set up Black as a separate formatter to gain experience with the configuration of both tools. Below, I’ll share why I chose these tools, how I configured them for my project, the challenges I faced, and the lessons I learned along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Tool Selection
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why Ruff?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Ruff&lt;/strong&gt; is a fast linter for Python that supports various linting rules from other linters (like Flake8 and Pyflakes) and offers significant performance improvements. It’s highly customizable, which allowed me to specify a mix of rules while ensuring compatibility with Black for formatting. Ruff’s design for speed and extensibility is ideal for projects that prioritize efficiency without sacrificing quality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ruff Documentation&lt;/strong&gt;: &lt;a href="https://github.com/charliermarsh/ruff" rel="noopener noreferrer"&gt;https://github.com/charliermarsh/ruff&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why Black?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Black&lt;/strong&gt; is a Python formatter that strictly enforces one formatting style, helping reduce discussions and inconsistencies over code styling. While Ruff offers basic formatting capabilities, Black’s dedicated approach provides a few advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Black enforces a strict, standard style that minimizes debates over code formatting.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Broad Adoption&lt;/strong&gt;: Black is widely used, making it easier to integrate into most development workflows, especially in collaborative projects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Black Documentation&lt;/strong&gt;: &lt;a href="https://github.com/psf/black" rel="noopener noreferrer"&gt;https://github.com/psf/black&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2. Project Setup
&lt;/h2&gt;

&lt;p&gt;To ensure that Ruff and Black worked seamlessly in &lt;strong&gt;ReadmeGenie&lt;/strong&gt;, I configured them in both &lt;code&gt;pyproject.toml&lt;/code&gt; and &lt;br&gt;
&lt;code&gt;.pre-commit-config.yaml&lt;/code&gt;, allowing developers to automatically format and lint code when making commits.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configuration for Ruff and Black in &lt;code&gt;pyproject.toml&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This setup ensures Ruff is used solely for linting and Black for formatting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# pyproject.toml&lt;/span&gt;
&lt;span class="c"&gt;# Set up black as formatter&lt;/span&gt;
&lt;span class="nn"&gt;[tool.black]&lt;/span&gt;
&lt;span class="py"&gt;line-length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;88&lt;/span&gt;
&lt;span class="py"&gt;target-version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"py311"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c"&gt;# Set up ruff as linter only&lt;/span&gt;
&lt;span class="nn"&gt;[tool.ruff]&lt;/span&gt;
&lt;span class="c"&gt;# Exclude directories that don’t need linting (e.g., virtual environments)&lt;/span&gt;
&lt;span class="py"&gt;exclude&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"venv/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"__pycache__/"&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;fix&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;


&lt;span class="c"&gt;# Enable specific linting rules&lt;/span&gt;
&lt;span class="py"&gt;select&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"F"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"E"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"W"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"I"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"S"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c"&gt;# Example codes: F=flake8, E=errors, W=warnings, B=bugbear, I=import, S=safety&lt;/span&gt;
&lt;span class="c"&gt;# Exclude Black-compatible rules to avoid conflicts with Black's formatting.&lt;/span&gt;
&lt;span class="py"&gt;ignore&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"E501"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"E203"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"E231"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c"&gt;# Exclude Black-incompatible style issues&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ignore&lt;/code&gt;: Black handles specific styling, so we excluded these rules in Ruff.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fix&lt;/code&gt;: Enables Ruff to fix issues where possible, leaving formatting to Black.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Adding Pre-commit Hook for Ruff and Black
&lt;/h3&gt;

&lt;p&gt;Using pre-commit hooks, I configured &lt;code&gt;.pre-commit-config.yaml&lt;/code&gt; to enforce linting and formatting on every commit:&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="c1"&gt;# .pre-commit-config.yaml&lt;/span&gt;
&lt;span class="na"&gt;repos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/psf/black&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;23.1.0&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;black&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;repo&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/charliermarsh/ruff-pre-commit&lt;/span&gt;
    &lt;span class="na"&gt;rev&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v0.7.1&lt;/span&gt;
    &lt;span class="na"&gt;hooks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ruff&lt;/span&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Running Ruff and Black from the Command Line
&lt;/h2&gt;

&lt;p&gt;With the above setup, you can use the following commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run Ruff&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  ruff check &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Run Black&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  black &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These commands apply fixes to all Python files, ensuring consistent styling and quality checks.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. VS Code Integration
&lt;/h2&gt;

&lt;p&gt;To automate Ruff and Black on save, I added the following configuration in &lt;code&gt;.vscode/settings.json&lt;/code&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="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Enable&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Black&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;formatter&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"python.formatting.provider"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"black"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Set&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;up&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Ruff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;linter&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"python.linting.enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"python.linting.ruffEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Ensure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Ruff&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;used&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;linter&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"python.linting.flake8Enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"python.linting.pylintEnabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Format&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;lint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;save&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.codeActionsOnSave"&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;"source.fixAll"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source.organizeImports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;This setup makes Black the default formatter and Ruff the only active linter in VS Code, allowing both to run &lt;br&gt;
automatically upon saving.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Findings and Fixes
&lt;/h2&gt;

&lt;p&gt;Once configured, Ruff and Black identified several issues:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Line Length (E501)&lt;/strong&gt;: Ruff initially flagged long lines, which Black auto-formatted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unused Imports and Variables&lt;/strong&gt;: Ruff caught several unused imports and variables.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Indentation and Styling Consistency&lt;/strong&gt;: Black applied consistent spacing and indentation, enhancing readability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Challenges
&lt;/h2&gt;

&lt;p&gt;One notable challenge was understanding that some styles are incompatible between Ruff and Black. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Line Length (E501)&lt;/strong&gt;: Ruff initially flagged long lines exceeding 88 characters, which Black handles by wrapping 
lines. To prevent conflicts, I added &lt;code&gt;E501&lt;/code&gt; to Ruff’s ignore list. Despite this, Ruff sometimes flagged E501 errors 
if Black didn’t apply the expected breakpoints. These discrepancies underscored the importance of adjusting each tool’s 
configuration and understanding where they may overlap.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Lessons Learned
&lt;/h2&gt;

&lt;p&gt;Using Ruff and Black together has been a great way to improve code quality. Here’s what I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency&lt;/strong&gt;: Black’s opinionated style reduces ambiguity in code styling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Automation&lt;/strong&gt;: Pre-commit hooks save time and ensure consistent formatting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Editor Integration&lt;/strong&gt;: Configuring Ruff and Black to run on save within VS Code streamlined development.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compatibility&lt;/strong&gt;: Learning how to resolve conflicts like &lt;code&gt;E501&lt;/code&gt; taught me about tool configurations and helped 
fine-tune project workflows.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>python</category>
      <category>opensource</category>
      <category>formatter</category>
      <category>linters</category>
    </item>
    <item>
      <title>Hacktoberfest week 3- Online Auction System</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Mon, 21 Oct 2024 00:13:30 +0000</pubDate>
      <link>https://dev.to/htsagara/hacktoberfest-week-3-online-auction-system-4knp</link>
      <guid>https://dev.to/htsagara/hacktoberfest-week-3-online-auction-system-4knp</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;During Week 3 of Hacktoberfest, I decided to contribute to a smaller but promising project: an Online Auction System. Though the project is still in its early stages, it already shows potential for growth, and I saw an opportunity to help improve its codebase. My task was to refactor the project by reducing redundant code and improving overall structure, making it more maintainable and scalable.&lt;/p&gt;

&lt;h2&gt;
  
  
  My task
&lt;/h2&gt;

&lt;p&gt;The core objective of my task was to identify repetitive code patterns and refactor them to make the codebase cleaner and easier to maintain. Code redundancy can lead to inconsistencies, make future updates harder, and increase the likelihood of bugs. By streamlining the code, I aimed to improve efficiency without altering functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Changes:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Centralized Asynchronous Error Handling:&lt;/strong&gt; One of the common issues in Express.js projects is repetitive &lt;code&gt;try-catch&lt;/code&gt; blocks for handling errors in asynchronous routes. To address this, I introduced a utility function called &lt;code&gt;asyncHandler&lt;/code&gt; that wraps all async functions, ensuring that any errors are automatically caught and handled consistently across the app.&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="c1"&gt;// utils/asyncHandler.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;asyncHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Internal server error&lt;/span&gt;&lt;span class="dl"&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="k"&gt;catch&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Error: &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="nx"&gt;message&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;errorMessage&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;asyncHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This change not only removed redundant &lt;code&gt;try-catch&lt;/code&gt; blocks but also made the code cleaner and more focused on the business logic, rather than error handling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Database Utility Functions:&lt;/strong&gt; The project had several routes that directly interacted with the database, often repeating similar queries. To reduce redundancy, I created a set of reusable utility functions to handle common database queries, such as fetching a buyer's information or retrieving transactions.&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="c1"&gt;// utils/dbUtils.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getBuyerById&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buyerId&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM Buyers WHERE Buyer_ID = $1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;buyerId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;rows&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="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="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getTransactionsByBuyerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buyerId&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;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT * FROM Transactions WHERE Buyer_ID = $1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;buyerId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getBuyerById&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getTransactionsByBuyerId&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, instead of writing SQL queries directly in each route, the codebase can rely on these utility functions, reducing duplication and making the logic easier to manage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Unified Response Handling:&lt;/strong&gt; Another area of improvement was response handling. Different routes had inconsistent patterns for sending success and error responses. To standardize this, I introduced utility functions like &lt;code&gt;send404&lt;/code&gt;, &lt;code&gt;send500&lt;/code&gt;, and &lt;code&gt;sendSuccess&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;Since the project is still in its early stages, I noticed some missing routes like &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, and &lt;code&gt;PUT&lt;/code&gt; that I could have easily create. However, I decided to stick with only my Issue was asking.&lt;/p&gt;

&lt;p&gt;Let me know in the comments, how do you usually deal with this kind of situations that you could contribute more...Do you open a new issue, do you just create it and do a Pull Request? I would love to read you experience.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>hacktoberfest</category>
      <category>javascript</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Hacktoberfest week 2- Shardeum Validator Dashboard</title>
      <dc:creator>Henrique Sagara</dc:creator>
      <pubDate>Fri, 11 Oct 2024 21:02:44 +0000</pubDate>
      <link>https://dev.to/htsagara/hacktoberfest-week-2-shardeum-validator-dashboard-dl</link>
      <guid>https://dev.to/htsagara/hacktoberfest-week-2-shardeum-validator-dashboard-dl</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;For the Hacktober week 2 I choose to work on an issue from Shardeum on their Validator Dashboard. Shardeum is a highly scalable, EVM-compatible blockchain that aims to offer low-cost, decentralized, and secure services. Its architecture is built on the principle of dynamic state sharding, where the network automatically expands by adding more validator nodes as the demand for resources increases, allowing Shardeum to maintain high throughput and decentralization.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Shardeum Validator Dashboard?
&lt;/h2&gt;

&lt;p&gt;The Shardeum Validator Dashboard is a tool that allows node operators to monitor and manage their validator nodes. These validator nodes play a key role in maintaining the network's consensus and processing transactions securely. The dashboard provides an interface for operators to check the health of their nodes, configure settings, and ensure they are functioning correctly in the Shardeum network.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Issue
&lt;/h2&gt;

&lt;p&gt;The Validator Dashboard relies on a number of environment variables for configuration, such as port numbers, IP addresses, and node settings. However, there was no comprehensive validation in place to ensure that these environment variables were correctly set before launching the dashboard. This lack of validation led to silent failures or misconfigurations, which could cause the node to malfunction or behave unexpectedly. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Invalid port numbers or IP addresses could prevent the node from communicating properly.&lt;/li&gt;
&lt;li&gt;A weak dashboard password could expose the node to security risks.&lt;/li&gt;
&lt;li&gt;Missing or incorrectly configured node parameters like &lt;code&gt;minNodes&lt;/code&gt; or &lt;code&gt;nodesPerConsensusGroup&lt;/code&gt; could lead to improper operation of the consensus mechanism.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;I created a reusable script &lt;code&gt;check-variables.sh&lt;/code&gt; that contains functions for validating each type of environment variable. This script can be sourced by other scripts (like &lt;code&gt;entrypoint.sh&lt;/code&gt; and &lt;code&gt;installer.sh&lt;/code&gt;), making the validation process centralized and easier to maintain.&lt;/p&gt;

&lt;p&gt;I hope it have a great impact in the Shardeum. I had a great time hacking in their repository. And their team was really nice providing help and information.&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>shardeum</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
