<?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>Forem: Chandrashekhar Kachawa</title>
    <description>The latest articles on Forem by Chandrashekhar Kachawa (@ctrix).</description>
    <link>https://forem.com/ctrix</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%2F890360%2Ff71f4453-fac6-4a94-a55d-7c10dac86856.jpg</url>
      <title>Forem: Chandrashekhar Kachawa</title>
      <link>https://forem.com/ctrix</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/ctrix"/>
    <language>en</language>
    <item>
      <title>System Design Fundamentals: From Monolith to Microservices</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Wed, 07 Jan 2026 09:11:37 +0000</pubDate>
      <link>https://forem.com/ctrix/system-design-fundamentals-from-monolith-to-microservices-33h7</link>
      <guid>https://forem.com/ctrix/system-design-fundamentals-from-monolith-to-microservices-33h7</guid>
      <description>&lt;p&gt;Choosing the right architecture is one of the most critical decisions you'll make when building a software application. It impacts how quickly you can ship features, how well your application scales, and how easy it is to maintain. Two of the most talked-about architectural patterns are the &lt;strong&gt;monolith&lt;/strong&gt; and &lt;strong&gt;microservices&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This isn't a battle of which is "better," but a discussion of trade-offs. Let's break down what each pattern means and explore the journey from one to the other.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Monolith: The All-in-One Application
&lt;/h2&gt;

&lt;p&gt;A monolithic architecture is the traditional way of building applications. It's a single, unified codebase where all the application's components—UI, business logic, data access layer—are developed, deployed, and scaled as a single unit.&lt;/p&gt;

&lt;p&gt;Imagine a large retail store. Everything is in one massive building: clothing, electronics, groceries, and the checkout counters. This is a monolith.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros of a Monolith
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity of Development:&lt;/strong&gt; When you're starting, having everything in one codebase is straightforward. There's no complex distributed system to worry about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy to Test:&lt;/strong&gt; End-to-end testing is simpler because you're testing a single, self-contained application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single Deployment:&lt;/strong&gt; You deploy one unit. This simplifies your CI/CD pipeline initially.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less Overhead:&lt;/strong&gt; No need to manage communication between different services.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of a Monolith
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tight Coupling:&lt;/strong&gt; As the application grows, components become tightly coupled and hard to isolate. A change in one part can have unintended consequences in another.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficult to Scale:&lt;/strong&gt; You have to scale the entire application, even if only one small part is a bottleneck. You can't scale the user authentication service independently of the image processing service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technology Stack Lock-in:&lt;/strong&gt; You're committed to a single technology stack. Adopting a new language or framework for a specific feature is difficult.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slowing Development Velocity:&lt;/strong&gt; As the codebase gets larger, it becomes harder to understand, compile times increase, and developer onboarding slows down.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Microservices: A Constellation of Services
&lt;/h2&gt;

&lt;p&gt;A microservice architecture breaks down an application into a collection of smaller, independent services. Each service is responsible for a specific business capability, runs in its own process, and communicates with other services over a network, typically via APIs.&lt;/p&gt;

&lt;p&gt;Think of it as a city. Instead of one giant building, you have a specialized hospital, a power plant, a water treatment facility, and a telecommunications tower. Each is independent, can be upgraded separately, but they all work together.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros of Microservices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Independent Scaling:&lt;/strong&gt; You can scale individual services based on their specific needs. If your video encoding service is under heavy load, you can scale just that service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Technology Freedom:&lt;/strong&gt; Each service can be built with the best technology for its job. You could have a service in Python for machine learning, another in Go for high-concurrency tasks, and another in Node.js for its API gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fault Isolation:&lt;/strong&gt; If one service fails, it doesn't necessarily bring down the entire application.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Development:&lt;/strong&gt; Teams can develop, deploy, and manage their services independently, leading to faster development cycles in large organizations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cons of Microservices
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Operational Complexity:&lt;/strong&gt; You're now managing a distributed system. This means more moving parts, more deployments, and a need for robust automation and monitoring.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Network Latency:&lt;/strong&gt; Communication between services over a network is slower than in-process calls within a monolith.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Consistency:&lt;/strong&gt; Maintaining data consistency across multiple services is a significant challenge. This often requires complex patterns like sagas or eventual consistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed Debugging:&lt;/strong&gt; Debugging a request that spans multiple services is significantly harder. Centralized logging and tracing become essential.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Migration: When and How?
&lt;/h2&gt;

&lt;p&gt;Very few applications should start with microservices. The operational overhead is often too high for a new product. Most successful microservice architectures begin life as a monolith that became successful enough to warrant a migration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So, when do you consider migrating?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scaling Bottlenecks:&lt;/strong&gt; You find that one part of your monolith is consuming most of the resources, and you need to scale it independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Slowdown:&lt;/strong&gt; Your teams are stepping on each other's toes, and deploying new features has become slow and risky.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need for Specialized Technology:&lt;/strong&gt; A new business requirement is best served by a technology that doesn't fit your monolithic stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Strangler Fig Pattern
&lt;/h3&gt;

&lt;p&gt;A "big bang" rewrite is almost always a bad idea. A proven, incremental approach is the &lt;strong&gt;Strangler Fig Pattern&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Identify a Seam:&lt;/strong&gt; Find a distinct piece of functionality in your monolith that you can "strangle" (e.g., user profile management).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Build a New Service:&lt;/strong&gt; Create a new microservice that implements this functionality.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Redirect Traffic:&lt;/strong&gt; Introduce a proxy or an API gateway that sits in front of the monolith. Initially, it just passes all traffic to the monolith. Then, you configure it to route traffic for the specific functionality (e.g., &lt;code&gt;/api/users/*&lt;/code&gt;) to your new microservice. The old monolithic code is still there, but it's no longer receiving traffic.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Repeat:&lt;/strong&gt; Continue this process, gradually building new services and routing traffic away from the monolith. Over time, the monolith shrinks and is eventually "strangled" by the new microservices.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;The choice between a monolith and microservices is about understanding trade-offs and choosing the right tool for the job at the right time.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with a monolith.&lt;/strong&gt; It's simpler and allows you to move quickly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep it modular.&lt;/strong&gt; Even within a monolith, practice good software design. Keep your code organized and your components loosely coupled. This will make a future migration much easier.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrate when the pain is real.&lt;/strong&gt; Don't jump to microservices because it's trendy. Do it when the limitations of your monolith are actively hindering your ability to scale your application or your team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding these fundamentals, you can make informed architectural decisions that set your application up for long-term success.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>ai</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Design APIs</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Fri, 02 Jan 2026 07:14:38 +0000</pubDate>
      <link>https://forem.com/ctrix/how-to-design-apis-oie</link>
      <guid>https://forem.com/ctrix/how-to-design-apis-oie</guid>
      <description>&lt;p&gt;As developers, we're often taught to build REST APIs following a standard set of conventions. We create multiple endpoints, use various HTTP verbs, and return a range of status codes like &lt;code&gt;200&lt;/code&gt;, &lt;code&gt;404&lt;/code&gt;, or &lt;code&gt;500&lt;/code&gt;. But what if this approach has fundamental flaws, especially for private, internal APIs?&lt;/p&gt;

&lt;p&gt;A senior engineer's perspective often challenges these conventions in favor of a more robust, predictable, and proprietary architecture. The goal is to build APIs that are not just functional, but also highly scalable, secure, and efficient. Let's explore how to achieve this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Architecture: A New Paradigm
&lt;/h3&gt;

&lt;p&gt;The foundation of a senior-level API design moves away from the classic REST model and embraces a more consolidated and efficient structure.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Move Away from REST for Private APIs
&lt;/h4&gt;

&lt;p&gt;Standard REST status codes can be misleading. A &lt;code&gt;500&lt;/code&gt; error might hide a predictable validation failure, and a &lt;code&gt;404&lt;/code&gt; doesn't distinguish between a mistyped URL and a resource that genuinely doesn't exist. For private APIs (those consumed by your own front end), we can do better. A proprietary design gives us more control and clarity.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Embrace a Single Endpoint and Batching
&lt;/h4&gt;

&lt;p&gt;Instead of a sprawling collection of URLs (&lt;code&gt;/users&lt;/code&gt;, &lt;code&gt;/posts&lt;/code&gt;, &lt;code&gt;/comments&lt;/code&gt;), consider using a &lt;strong&gt;single &lt;code&gt;/api&lt;/code&gt; endpoint&lt;/strong&gt; that only accepts &lt;code&gt;POST&lt;/code&gt; requests. This might sound counterintuitive, but it unlocks a powerful capability: &lt;strong&gt;request batching&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Client applications can collect multiple actions (e.g., fetching user data, posting a comment, updating a setting) and send them to the server in a single network call. By batching requests at short intervals (e.g., every 50ms), you dramatically reduce network overhead and improve perceived performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. A Clean, Folder-Based Structure
&lt;/h4&gt;

&lt;p&gt;To keep the codebase organized, each API should live in its own folder. This self-contained module should include at least two key files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;handler.ts&lt;/code&gt;&lt;/strong&gt;: The file containing the core execution logic for the API.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;constants.ts&lt;/code&gt;&lt;/strong&gt;: A metadata file that defines the API's behavior, such as its rate-limiting configuration and, most importantly, its validation schemas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Request Flow: Security and Predictability First
&lt;/h3&gt;

&lt;p&gt;A senior engineer knows that an API's primary responsibility is to be secure and predictable. This is achieved by establishing a strict, ordered flow for every incoming request.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Validation: The Most Critical Step
&lt;/h4&gt;

&lt;p&gt;Before your handler's logic ever runs, you must &lt;strong&gt;strictly validate the request body&lt;/strong&gt;. This is non-negotiable. Tools like &lt;strong&gt;Zod&lt;/strong&gt; are perfect for this, allowing you to define a precise schema for the expected input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/content/blog/api/some-api/constants.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zod&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SomeApiInputSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;280&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;Strict validation is your first line of defense against a host of vulnerabilities, including NoSQL injections. If the incoming data doesn't match the schema, the request is rejected immediately. No exceptions.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Smart Rate Limiting
&lt;/h4&gt;

&lt;p&gt;Rate limiting prevents abuse and ensures stability. The strategy should differ based on who is making the request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Unauthorized Users&lt;/strong&gt;: Rate limit based on their &lt;strong&gt;IP address&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Authorized Users&lt;/strong&gt;: Rate limit based on their &lt;strong&gt;Account ID&lt;/strong&gt; and the &lt;strong&gt;specific API&lt;/strong&gt; they are calling. This is more granular and fair.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pro-tip:&lt;/strong&gt; You can embed the &lt;code&gt;AccountID&lt;/code&gt; in a JWT-signed token. This allows your rate-limiting middleware to extract the ID without needing a costly database lookup, making the process incredibly fast.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Authorization: The First Line of Code
&lt;/h4&gt;

&lt;p&gt;Security shouldn't be an afterthought; it should be the very first thing you check. Every API handler should begin with an explicit authorization check.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/content/blog/api/some-api/handler.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;requireAdmin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;~/lib/auth&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SomeApiInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;requireAdmin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Throws an error if not an admin&lt;/span&gt;

  &lt;span class="c1"&gt;// ... rest of the logic&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;requireAdmin&lt;/code&gt; or &lt;code&gt;requireUser&lt;/code&gt; function acts as a gatekeeper, ensuring that the rest of your code only executes if the user has the necessary permissions.&lt;/p&gt;

&lt;h3&gt;
  
  
  End-to-End Type Safety: The Holy Grail
&lt;/h3&gt;

&lt;p&gt;While input schemas provide security, &lt;strong&gt;output schemas&lt;/strong&gt; provide type safety and a superior developer experience. By defining a Zod schema for the data your API returns, you create a contract.&lt;/p&gt;

&lt;p&gt;This contract can then be shared with your front-end application. Using a type generator, you can create a fully typed, end-to-end system. When the back end changes an output shape, the front-end code will immediately show a TypeScript error, catching bugs at compile time, not in production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trade-Offs and Considerations
&lt;/h3&gt;

&lt;p&gt;This opinionated design is powerful, but it's not a silver bullet. It's crucial to understand the trade-offs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Improved Performance:&lt;/strong&gt; Request batching dramatically reduces network latency for chatty applications.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Enhanced Developer Experience:&lt;/strong&gt; End-to-end type safety catches bugs early and makes development a breeze.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Superior Security:&lt;/strong&gt; A validation-first and authorization-first approach hardens your API from the ground up.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Cons
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Breaks REST Conventions:&lt;/strong&gt; This is the biggest drawback. You lose standard HTTP caching, semantic verb meanings (&lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;), and browser-friendly testing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Poor for Public APIs:&lt;/strong&gt; This pattern is ill-suited for third-party developers who expect standard RESTful interfaces.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Tooling Complexity:&lt;/strong&gt; Standard API tools like Swagger or Postman are built for REST and may not integrate seamlessly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This architecture, similar to what libraries like tRPC popularize, is an excellent choice for modern, tightly-coupled applications where you control both the client and server. For public-facing APIs, sticking to REST is often the better choice.&lt;/p&gt;

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

&lt;p&gt;Designing APIs like a senior engineer means prioritizing predictability, security, and performance. By moving away from traditional REST for private services and embracing concepts like single endpoints, request batching, strict validation, and end-to-end type safety, you can build a back-end architecture that is not only robust and scalable but also a joy to work with.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>design</category>
      <category>api</category>
    </item>
    <item>
      <title>A Practical Guide to Backing Up Your Linux Server: PostgreSQL, Jenkins, and More</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Wed, 31 Dec 2025 06:41:41 +0000</pubDate>
      <link>https://forem.com/ctrix/a-practical-guide-to-backing-up-your-linux-server-postgresql-jenkins-and-more-47lb</link>
      <guid>https://forem.com/ctrix/a-practical-guide-to-backing-up-your-linux-server-postgresql-jenkins-and-more-47lb</guid>
      <description>&lt;p&gt;As developers and system administrators, we build and manage services that are critical to our applications. But what happens when disaster strikes? A hardware failure, a software bug, or even human error can lead to catastrophic data loss. This is where a robust backup strategy isn't just a good idea—it's a necessity.&lt;/p&gt;

&lt;p&gt;This guide provides a practical, hands-on approach to backing up some of the most common and critical services running on a Linux server: PostgreSQL databases, Jenkins automation servers, and other essential system files.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Backing Up Your PostgreSQL Database
&lt;/h3&gt;

&lt;p&gt;Your database is often the most critical component of your application stack. &lt;code&gt;pg_dump&lt;/code&gt; is the standard, powerful utility for creating consistent backups of a PostgreSQL database.&lt;/p&gt;

&lt;h4&gt;
  
  
  On the Server: Creating the Backup
&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;pg_dump&lt;/code&gt; command connects to your database and writes its contents to a file. You can run this directly from your server's command line.&lt;/p&gt;

&lt;p&gt;A basic command looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; your_user &lt;span class="nt"&gt;-d&lt;/span&gt; your_db &lt;span class="nt"&gt;-f&lt;/span&gt; backup.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-U your_user&lt;/code&gt;: The PostgreSQL user to connect as.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-d your_db&lt;/code&gt;: The name of the database to back up.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f backup.sql&lt;/code&gt;: The output file for the backup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more flexibility during restoration, it's highly recommended to use the custom format (&lt;code&gt;-F c&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; your_user &lt;span class="nt"&gt;-d&lt;/span&gt; your_db &lt;span class="nt"&gt;-F&lt;/span&gt; c &lt;span class="nt"&gt;-f&lt;/span&gt; backup.dump
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This format is compressed and allows for more advanced restoration options with the &lt;code&gt;pg_restore&lt;/code&gt; utility, such as reordering or restoring specific objects.&lt;/p&gt;

&lt;h4&gt;
  
  
  Downloading the Backup to Your Local PC
&lt;/h4&gt;

&lt;p&gt;Once you have the backup file on your server, you need a secure way to download it to your local machine. The &lt;code&gt;scp&lt;/code&gt; (Secure Copy) command is perfect for this, as it transfers files over an encrypted SSH connection.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;scp user@your_server_ip:/path/to/backup.dump &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;user@your_server_ip&lt;/code&gt;: Your username and the IP address of your server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/path/to/backup.dump&lt;/code&gt;: The full path to the backup file on the server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.&lt;/code&gt;: The destination on your local machine (the current directory).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip: Using a Specific SSH Key&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your server requires a specific SSH key for authentication, you can provide it using the &lt;code&gt;-i&lt;/code&gt; (identity file) flag. Remember to ensure your key file has secure permissions (&lt;code&gt;chmod 600 /path/to/key&lt;/code&gt;).&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;scp &lt;span class="nt"&gt;-i&lt;/span&gt; /path/to/your/private_key user@your_server_ip:/path/to/backup.dump &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;For an even cleaner workflow, consider adding an entry to your SSH config file (&lt;code&gt;~/.ssh/config&lt;/code&gt;):&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host my-prod-server
    HostName your_server_ip
    User user
    IdentityFile /path/to/your/private_key
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;With this config, your command simplifies to &lt;code&gt;scp my-prod-server:/path/to/backup.dump .&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Backing Up Your Jenkins Server
&lt;/h3&gt;

&lt;p&gt;Jenkins stores all its data—including job configurations, build history, plugins, and system settings—in a single directory known as &lt;code&gt;JENKINS_HOME&lt;/code&gt;. Backing up Jenkins is as simple as creating an archive of this directory.&lt;/p&gt;

&lt;p&gt;First, find your &lt;code&gt;JENKINS_HOME&lt;/code&gt; directory. On most Linux systems, it's located at &lt;code&gt;/var/lib/jenkins&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can use the &lt;code&gt;tar&lt;/code&gt; utility to create a compressed archive:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czvf&lt;/span&gt; jenkins_backup_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz /var/lib/jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-c&lt;/code&gt;: Create a new archive.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-z&lt;/code&gt;: Compress the archive with gzip.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-v&lt;/code&gt;: Verbosely list the files processed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-f&lt;/code&gt;: Specifies the output archive file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(date +%F)&lt;/code&gt;: This handy command substitution appends the current date (YYYY-MM-DD) to the filename.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just like with the database backup, you can use &lt;code&gt;scp&lt;/code&gt; to download this &lt;code&gt;jenkins_backup.tar.gz&lt;/code&gt; file to your local machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Backing Up Essential System Files
&lt;/h3&gt;

&lt;p&gt;Beyond specific services, your server has other critical files, especially in &lt;code&gt;/etc&lt;/code&gt; (system-wide configurations) and &lt;code&gt;/home&lt;/code&gt; (user data). You can use &lt;code&gt;tar&lt;/code&gt; to back these up as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czvf&lt;/span&gt; system_backup_&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.tar.gz /etc /home /var/www
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command archives the configuration directory, all user home directories, and a common web root directory (&lt;code&gt;/var/www&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Automating Your Backups with Cron
&lt;/h3&gt;

&lt;p&gt;Manual backups are better than no backups, but automated backups are what give you true peace of mind. On Linux, the &lt;code&gt;cron&lt;/code&gt; daemon is the standard way to schedule recurring tasks.&lt;/p&gt;

&lt;p&gt;First, create a simple shell script to perform the backups.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;backup_script.sh&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Set backup directory&lt;/span&gt;
&lt;span class="nv"&gt;BACKUP_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/backups"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;

&lt;span class="c"&gt;# Timestamp&lt;/span&gt;
&lt;span class="nv"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Backup PostgreSQL&lt;/span&gt;
pg_dump &lt;span class="nt"&gt;-U&lt;/span&gt; your_user &lt;span class="nt"&gt;-d&lt;/span&gt; your_db &lt;span class="nt"&gt;-F&lt;/span&gt; c &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;/db_backup_&lt;span class="nv"&gt;$TIMESTAMP&lt;/span&gt;.dump

&lt;span class="c"&gt;# Backup Jenkins&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;/jenkins_backup_&lt;span class="nv"&gt;$TIMESTAMP&lt;/span&gt;.tar.gz /var/lib/jenkins

&lt;span class="c"&gt;# Backup system files&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-czf&lt;/span&gt; &lt;span class="nv"&gt;$BACKUP_DIR&lt;/span&gt;/system_backup_&lt;span class="nv"&gt;$TIMESTAMP&lt;/span&gt;.tar.gz /etc /home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the script executable: &lt;code&gt;chmod +x backup_script.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, open your crontab to edit it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a line to schedule the script. For example, to run it every day at 2:00 AM:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0 2 * * * /path/to/your/backup_script.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Regularly backing up your services is one of the most important responsibilities of managing a server. With tools like &lt;code&gt;pg_dump&lt;/code&gt;, &lt;code&gt;tar&lt;/code&gt;, &lt;code&gt;scp&lt;/code&gt;, and &lt;code&gt;cron&lt;/code&gt;, you can create a powerful, automated, and secure backup strategy.&lt;/p&gt;

&lt;p&gt;But remember the golden rule: &lt;strong&gt;a backup is worthless until you have successfully tested a restoration.&lt;/strong&gt; Periodically practice restoring your backups to a staging environment to ensure they work when you need them most.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>backup</category>
      <category>ubuntu</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Mastering Real-Time Communication with Socket.IO Rooms</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Mon, 22 Dec 2025 04:57:57 +0000</pubDate>
      <link>https://forem.com/ctrix/mastering-real-time-communication-with-socketio-rooms-4bom</link>
      <guid>https://forem.com/ctrix/mastering-real-time-communication-with-socketio-rooms-4bom</guid>
      <description>&lt;p&gt;Socket.IO is a powerful library that enables real-time, bidirectional, and event-based communication between web clients and servers. While broadcasting messages to all connected clients is simple, most real-world applications require sending messages to specific groups of users. This is where Socket.IO's "rooms" feature shines.&lt;/p&gt;

&lt;p&gt;This guide will walk you through setting up and using rooms in Node.js, covering everything from creation to managing public and private spaces.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Rooms?
&lt;/h2&gt;

&lt;p&gt;A room is a server-side concept that allows you to group sockets together. Sockets can &lt;code&gt;join&lt;/code&gt; and &lt;code&gt;leave&lt;/code&gt; rooms, and you can broadcast messages to all sockets within a specific room. A single socket can be in multiple rooms at once.&lt;/p&gt;

&lt;p&gt;Every socket automatically joins a room identified by its own unique &lt;code&gt;socket.id&lt;/code&gt;. This is useful for sending private messages to a specific user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Basic Server
&lt;/h2&gt;

&lt;p&gt;First, let's set up a basic Node.js server with Express and Socket.IO.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm &lt;span class="nb"&gt;install &lt;/span&gt;express socket.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, create your main server file (e.g., &lt;code&gt;server.js&lt;/code&gt;):&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;// server.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createServer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Server&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;socket.io&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="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&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;httpServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&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;io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;httpServer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Allow all origins for simplicity&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;connection&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`A user connected: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="c1"&gt;// Listen for a custom event to join a room&lt;/span&gt;
  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;joinRoom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&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;log&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;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; joined room: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;roomName&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="c1"&gt;// Broadcast to the specific room&lt;/span&gt;
    &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`Welcome &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; to the &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; room!`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;disconnect&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`A user disconnected: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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="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;PORT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;httpServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PORT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Server is running on port &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;PORT&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Room Creation and Uniqueness
&lt;/h2&gt;

&lt;p&gt;You might have noticed we never explicitly "created" a room. &lt;strong&gt;Rooms are created implicitly when the first socket joins them.&lt;/strong&gt; They are automatically disposed of when the last socket leaves.&lt;/p&gt;

&lt;p&gt;The uniqueness of a room is simply based on its name (a string). It's your application's responsibility to manage these names.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Ensure Unique Rooms
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;For Public Rooms:&lt;/strong&gt; Use predefined, human-readable names like &lt;code&gt;general&lt;/code&gt;, &lt;code&gt;support&lt;/code&gt;, or &lt;code&gt;news&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;For Private Rooms (e.g., Direct Messages):&lt;/strong&gt; You need a consistent and unique identifier for the two users involved. A common strategy is to combine their unique user IDs in a predictable order.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, if you have two user IDs, &lt;code&gt;userA_id&lt;/code&gt; and &lt;code&gt;userB_id&lt;/code&gt;:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPrivateRoomName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Sort the IDs to ensure the room name is always the same&lt;/span&gt;
  &lt;span class="c1"&gt;// regardless of who initiates the chat.&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;id2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPrivateRoomName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userA_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;userB_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "userA_id-userB_id"&lt;/span&gt;
&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This guarantees that any two users will always share the same private room.&lt;/p&gt;

&lt;h2&gt;
  
  
  Public vs. Private Rooms
&lt;/h2&gt;

&lt;p&gt;The distinction between public and private rooms isn't a feature of Socket.IO itself but a pattern implemented in your application's logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Public Rooms
&lt;/h3&gt;

&lt;p&gt;A public room is one that any user can join. The client-side code would typically have a list of available public rooms, and the user can choose one to join.&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;// Client-side code&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;socket&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;io&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// User joins the 'general' chat room&lt;/span&gt;
&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;joinRoom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;general&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&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;
  
  
  Private Rooms
&lt;/h3&gt;

&lt;p&gt;A private room has controlled access. You should always validate on the server whether a user is authorized to join a specific room. This is crucial for security.&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;// Server-side code&lt;/span&gt;
&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;joinPrivateRoom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;roomId&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="c1"&gt;// Example: Check if the user is authenticated and has permission&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isAuthorized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;checkUserAuthorization&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isAuthorized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;io&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; has joined the private discussion.`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You are not authorized to join this room.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to Use Socket.IO Rooms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Chat Applications:&lt;/strong&gt; The most obvious use case. Create rooms for group chats, direct messages, or topic-based discussions.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Real-Time Collaboration:&lt;/strong&gt; Think Google Docs. Each document can be a room, and all edits are broadcast only to the users currently viewing that document.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Multiplayer Games:&lt;/strong&gt; Group players in a game session into a room. Game state updates are sent only to players in that session, not everyone on the server.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Notification Systems:&lt;/strong&gt; When a user performs an action, you can send a notification to a room containing their teammates or followers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When NOT to Use Rooms
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Simple Data Fetching:&lt;/strong&gt; If a client just needs to pull data from the server, a standard REST or GraphQL API is more appropriate. WebSockets add unnecessary complexity.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Broadcasting to Everyone:&lt;/strong&gt; If you want to send a message to every single connected client, you don't need a room. Just use &lt;code&gt;io.emit()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Unidirectional Updates:&lt;/strong&gt; If you only need to send data from the server to the client (e.g., stock tickers, news feeds), consider &lt;strong&gt;Server-Sent Events (SSE)&lt;/strong&gt;. SSE is a simpler protocol built on top of HTTP and is often sufficient for these scenarios.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Socket.IO rooms are a fundamental concept for building scalable, real-time applications. By grouping sockets, you can efficiently manage communication and ensure that messages are delivered only to the intended recipients. The key is to remember that rooms are a server-side abstraction, and their "privacy" or "publicity" is determined entirely by your application's logic and authorization checks.&lt;/p&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Hoppscotch: The Modern, Lightweight Alternative to Postman and Bruno</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Thu, 18 Dec 2025 10:30:12 +0000</pubDate>
      <link>https://forem.com/ctrix/hoppscotch-the-modern-lightweight-alternative-to-postman-and-bruno-3gkm</link>
      <guid>https://forem.com/ctrix/hoppscotch-the-modern-lightweight-alternative-to-postman-and-bruno-3gkm</guid>
      <description>&lt;p&gt;In the world of software development, API client tools are as essential as a good code editor. For years, Postman has been the reigning champion, the default choice for developers everywhere. More recently, tools like Bruno have emerged, catering to developers who prefer a more git-native, offline-first approach. But what if there was a tool that combined the best of both worlds—a fast, web-native experience with the flexibility of open-source?&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://hoppscotch.io/" rel="noopener noreferrer"&gt;Hoppscotch&lt;/a&gt;, the open-source API development suite that’s rapidly gaining popularity. It’s a powerful, lightweight, and beautifully designed tool that challenges the way we think about API testing and collaboration.&lt;/p&gt;

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

&lt;p&gt;At its core, Hoppscotch is a web-based API client. You can simply navigate to their website and start making requests immediately—no installation required. But don't let its simplicity fool you. It's a feature-rich platform that supports REST, GraphQL, and real-time protocols like WebSocket, Socket.IO, and MQTT.&lt;/p&gt;

&lt;p&gt;Being open-source, it offers a level of transparency and community involvement that proprietary tools can't match. Plus, for teams concerned with data privacy, it can be entirely self-hosted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hoppscotch vs. Postman: A Battle of Philosophies
&lt;/h2&gt;

&lt;p&gt;Postman is a powerful, mature platform, but its recent focus on cloud features and team collaboration has come at a cost. The desktop application can feel heavy, slow to start, and many of its most compelling features are locked behind a paid subscription and require an account.&lt;/p&gt;

&lt;p&gt;Here’s how Hoppscotch offers a refreshing alternative:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Performance and Accessibility&lt;/strong&gt;: Hoppscotch is web-first, meaning it's incredibly fast and accessible from any device with a browser. There's no hefty installation, and it launches instantly. For those who prefer a desktop experience, it's available as a Progressive Web App (PWA).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;True Open Source and Cost-Effectiveness&lt;/strong&gt;: Hoppscotch is free and open-source. While they offer a paid cloud service for teams, the core functionality is available to everyone. Postman's free tier has become increasingly restrictive, pushing users towards paid plans for what many consider basic features.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Collaboration Model&lt;/strong&gt;: Hoppscotch allows for creating team workspaces and sharing collections without forcing every user into a complex, account-based ecosystem. You can instantly share a request via a link. Postman’s collaboration is powerful but tightly integrated with its paid cloud services.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;User Experience&lt;/strong&gt;: Hoppscotch boasts a clean, modern, and intuitive UI that feels less cluttered than Postman's feature-packed interface.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;For developers tired of Postman's sluggish performance and account-walled garden, Hoppscotch is a breath of fresh air.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Hoppscotch vs. Bruno: A Different Kind of Open Source
&lt;/h2&gt;

&lt;p&gt;Bruno is another fantastic open-source tool that has gained a following for its unique, git-friendly approach. It stores all collection data directly on your local filesystem, allowing you to version control your API tests just like your code. This is a brilliant concept, but it serves a different workflow compared to Hoppscotch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Philosophy&lt;/strong&gt;: Hoppscotch is &lt;strong&gt;web-first&lt;/strong&gt;, designed for speed and seamless online access. Bruno is &lt;strong&gt;local-first&lt;/strong&gt;, designed for developers who want to manage everything within their Git repository.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Collaboration&lt;/strong&gt;: Collaboration in Bruno happens via pull requests, which is perfect for teams deeply embedded in a GitOps workflow. Hoppscotch offers more immediate, real-time collaboration through shared workspaces and links, which is often faster for quick debugging or pairing sessions.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Accessibility&lt;/strong&gt;: With Hoppscotch, you can send a teammate a URL, and they can immediately see and interact with the request. With Bruno, your teammate needs to have the app installed and pull the latest changes from the repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If your team's identity is "everything-in-Git," Bruno is a perfect fit. If your team values speed, accessibility, and real-time web-based tools, Hoppscotch has the edge.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Key Features That Make Hoppscotch Shine
&lt;/h2&gt;

&lt;p&gt;Beyond the comparisons, Hoppscotch has a feature set that makes it a compelling tool on its own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Multi-Protocol Support&lt;/strong&gt;: It's not just for REST. First-class support for &lt;strong&gt;GraphQL&lt;/strong&gt; (with a schema explorer) and real-time protocols is a huge advantage.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pre-Request Scripts &amp;amp; Tests&lt;/strong&gt;: Write JavaScript code to run before a request is sent or to validate the response, similar to Postman.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Environments &amp;amp; Variables&lt;/strong&gt;: Manage different environments (like development, staging, production) with ease.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;PWA for Offline Use&lt;/strong&gt;: Install Hoppscotch as a PWA to get a desktop-like experience and use it even when you're offline.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Self-Hosting&lt;/strong&gt;: For ultimate data privacy and control, you can host your own instance of Hoppscotch with just a few commands.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;The choice of an API client depends heavily on your team's workflow and priorities.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Postman&lt;/strong&gt; remains a powerful, enterprise-grade tool, but its performance and cost can be significant drawbacks.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Bruno&lt;/strong&gt; is the ideal choice for developers who want to live entirely within their Git workflow, treating API collections as code.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Hoppscotch&lt;/strong&gt; carves out a unique and compelling niche. It's the perfect tool for developers and teams who prioritize speed, accessibility, and a clean user experience, all wrapped in a transparent, open-source package.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you haven't tried it yet, you owe it to yourself to give Hoppscotch a spin. It might just become your new default API client.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Mastering Python Monorepos: A Practical Guide</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Fri, 12 Dec 2025 10:33:08 +0000</pubDate>
      <link>https://forem.com/ctrix/mastering-python-monorepos-a-practical-guide-2b4</link>
      <guid>https://forem.com/ctrix/mastering-python-monorepos-a-practical-guide-2b4</guid>
      <description>&lt;p&gt;As projects grow, managing dependencies and shared code across multiple repositories can become a significant challenge. Code gets duplicated, dependency versions drift apart, and coordinating changes becomes a complex dance. A monorepo—a single repository containing multiple distinct projects—offers a powerful solution to these problems.&lt;/p&gt;

&lt;p&gt;This guide will walk you through building a scalable Python monorepo, integrating different services like FastAPI and Apache Airflow, and maintaining high code quality with modern tools like Ruff.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Monorepo?
&lt;/h2&gt;

&lt;p&gt;Before we dive in, let's clarify the benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Dependency Management:&lt;/strong&gt; A single set of dependencies at the root level (or well-defined per-project dependencies) prevents version conflicts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Commits:&lt;/strong&gt; Changes across multiple services can be made in a single commit, ensuring consistency and simplifying rollbacks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Seamless Code Sharing:&lt;/strong&gt; Reusing code between projects is as simple as a local import, eliminating the need for a separate package registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unified Tooling:&lt;/strong&gt; A single configuration for linting, formatting, and testing can be enforced across the entire codebase.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Designing the Monorepo Structure
&lt;/h2&gt;

&lt;p&gt;A clean structure is key to a maintainable monorepo. Here’s a proven layout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-monorepo/
├── .gitignore
├── pyproject.toml      # Root config for dependencies and tools (Ruff)
├── packages/
│   └── shared_library/
│       ├── pyproject.toml  # Makes this an installable package
│       └── src/
│           └── shared_library/
│               ├── __init__.py
│               └── core.py       # Shared business logic
└── services/
    ├── fastapi_app/
    │   ├── pyproject.toml
    │   └── src/
    │       └── fastapi_app/
    │           └── main.py
    └── airflow_dags/
        ├── pyproject.toml
        └── src/
            └── airflow_dags/
                └── example_dag.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;packages/&lt;/code&gt;&lt;/strong&gt;: Contains local Python packages intended to be shared across different services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;services/&lt;/code&gt;&lt;/strong&gt;: Contains the actual applications, like your FastAPI microservice or Airflow DAGs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/strong&gt;: The root &lt;code&gt;pyproject.toml&lt;/code&gt; is crucial for centralizing tool configuration and, optionally, base dependencies.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Creating a Shared Package
&lt;/h2&gt;

&lt;p&gt;The magic of a monorepo lies in its ability to share code effortlessly. Let's make &lt;code&gt;shared_library&lt;/code&gt; an installable package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;packages/shared_library/pyproject.toml&lt;/code&gt;&lt;/strong&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;[project]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"shared_library"&lt;/span&gt;
&lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.1.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;packages/shared_library/src/shared_library/core.py&lt;/code&gt;&lt;/strong&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="c1"&gt;# Example shared function
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_greeting&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="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello from the shared library!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, any service in your monorepo can install this package in &lt;strong&gt;editable mode&lt;/strong&gt;. This means changes to the library code are immediately reflected in the services that use it, without needing a re-installation.&lt;/p&gt;

&lt;p&gt;From the root of the monorepo, you would typically install this into the virtual environment of a service that needs it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Example: Installing for the fastapi_app&lt;/span&gt;
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; packages/shared_library
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Integrating Your Services
&lt;/h2&gt;

&lt;p&gt;Let's see how our FastAPI app and Airflow DAGs can use the &lt;code&gt;shared_library&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. FastAPI Microservice
&lt;/h3&gt;

&lt;p&gt;Your FastAPI app can now import directly from the shared code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;services/fastapi_app/src/fastapi_app/main.py&lt;/code&gt;&lt;/strong&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;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;shared_library.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_greeting&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nd"&gt;@app.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;/&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;read_root&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;greeting&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you run this FastAPI application, it will execute the code from &lt;code&gt;shared_library&lt;/code&gt; as if it were a standard installed package.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Apache Airflow DAGs
&lt;/h3&gt;

&lt;p&gt;Similarly, your Airflow DAGs can pull in shared logic for tasks, connections, or configurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;services/airflow_dags/src/airflow_dags/example_dag.py&lt;/code&gt;&lt;/strong&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;from&lt;/span&gt; &lt;span class="n"&gt;__future__&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;annotations&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pendulum&lt;/span&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;airflow.models.dag&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DAG&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;airflow.operators.python&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PythonOperator&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;shared_library.core&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_greeting&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;print_greeting&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 simple Python function to be executed by Airflow.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_greeting&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;DAG&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;dag_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;example_shared_library_dag&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;start_date&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;pendulum&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UTC&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;catchup&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="n"&gt;schedule&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;tags&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;example&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="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;dag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nc"&gt;PythonOperator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;task_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;print_shared_greeting&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;python_callable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;print_greeting&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;For Airflow to find your &lt;code&gt;shared_library&lt;/code&gt;, you must ensure it's installed in the Python environment where your Airflow scheduler and workers are running.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tooling and Best Practices with Ruff
&lt;/h2&gt;

&lt;p&gt;A monorepo is the perfect environment for standardized tooling. &lt;code&gt;Ruff&lt;/code&gt; is an incredibly fast linter and formatter that can be configured at the root of your project.&lt;/p&gt;

&lt;p&gt;In your root &lt;strong&gt;&lt;code&gt;pyproject.toml&lt;/code&gt;&lt;/strong&gt;, add the following configuration:&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.ruff]&lt;/span&gt;
&lt;span class="c"&gt;# Enable Pyflakes, Pycodestyle, and isort 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;"E"&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;"I"&lt;/span&gt;&lt;span class="p"&gt;]&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="nn"&gt;[tool.ruff.lint]&lt;/span&gt;
&lt;span class="c"&gt;# Define source directories for Ruff to check&lt;/span&gt;
&lt;span class="py"&gt;include&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s"&gt;"packages/shared_library/src/**/*.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"services/fastapi_app/src/**/*.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"services/airflow_dags/src/**/*.py"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="nn"&gt;[tool.ruff.format]&lt;/span&gt;
&lt;span class="c"&gt;# Use black-compatible formatting&lt;/span&gt;
&lt;span class="py"&gt;quote-style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"double"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can run Ruff from the root of your monorepo to check and format your entire codebase with a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check for linting errors&lt;/span&gt;
ruff check &lt;span class="nb"&gt;.&lt;/span&gt;

&lt;span class="c"&gt;# Format all files&lt;/span&gt;
ruff format &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures that all your Python code, regardless of which service it belongs to, adheres to the same quality standards.&lt;/p&gt;

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

&lt;p&gt;Adopting a Python monorepo requires a shift in mindset but offers immense rewards in terms of maintainability, consistency, and development speed. By creating a logical structure with shared packages and enforcing unified tooling with &lt;code&gt;Ruff&lt;/code&gt;, you can build a robust foundation that scales with your projects. This architecture empowers you to manage complex systems like co-dependent microservices and data pipelines with greater confidence and efficiency.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>python</category>
      <category>monorepo</category>
      <category>programming</category>
    </item>
    <item>
      <title>Demystifying Retrieval-Augmented Generation (RAG)</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Thu, 11 Dec 2025 07:47:38 +0000</pubDate>
      <link>https://forem.com/ctrix/demystifying-retrieval-augmented-generation-rag-2bh6</link>
      <guid>https://forem.com/ctrix/demystifying-retrieval-augmented-generation-rag-2bh6</guid>
      <description>&lt;p&gt;Large Language Models (LLMs) have revolutionized the way we interact with information, but they have a fundamental limitation: their knowledge is frozen at the time they were trained. They can't access real-time information and can sometimes "hallucinate" or invent facts. This is where Retrieval-Augmented Generation (RAG) comes in.&lt;/p&gt;

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

&lt;p&gt;RAG is a technique that enhances the capabilities of LLMs by connecting them to external knowledge bases. In simple terms, instead of relying solely on its pre-trained data, the model first &lt;em&gt;retrieves&lt;/em&gt; relevant information from a specific dataset (like your company's internal documents, a database, or a specific website) and then uses this information to &lt;em&gt;generate&lt;/em&gt; a more accurate and context-aware response.&lt;/p&gt;

&lt;p&gt;It’s like an open-book exam for an LLM. The model doesn't have to memorize everything; it just needs to know how to look up the right information before answering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Do We Need It?
&lt;/h2&gt;

&lt;p&gt;The primary motivation for RAG is to overcome the inherent limitations of standalone LLMs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Knowledge Cutoffs:&lt;/strong&gt; LLMs are unaware of events or data created after their training. RAG provides a direct line to up-to-the-minute information.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Hallucinations:&lt;/strong&gt; When an LLM doesn't know an answer, it might generate a plausible-sounding but incorrect response. RAG grounds the model in factual data, significantly reducing the chances of hallucination.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Lack of Specificity:&lt;/strong&gt; A general-purpose LLM knows a lot about everything but may lack deep knowledge of a specific, niche domain (like your organization's internal policies or a technical knowledge base). RAG allows you to inject this domain-specific expertise.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Verifiability:&lt;/strong&gt; With RAG, you can often cite the sources used to generate the answer, providing a way for users to verify the information.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How Does RAG Work?
&lt;/h2&gt;

&lt;p&gt;The RAG process can be broken down into a few key steps. Let's imagine we're building a chatbot to answer questions based on a set of internal documents.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Indexing (The Setup Phase)
&lt;/h3&gt;

&lt;p&gt;Before you can retrieve anything, you need to prepare your knowledge base.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Load Documents:&lt;/strong&gt; Your documents (e.g., PDFs, Markdown files, database records) are loaded.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Chunking:&lt;/strong&gt; Each document is broken down into smaller, manageable chunks of text.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Embedding:&lt;/strong&gt; Each chunk is converted into a numerical representation (a vector embedding) using an embedding model. These embeddings capture the semantic meaning of the text.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Storing:&lt;/strong&gt; The chunks and their corresponding embeddings are stored in a specialized database called a &lt;strong&gt;vector store&lt;/strong&gt;, which is optimized for fast similarity searches.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This indexing process is typically done offline and only needs to be repeated when your source documents change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Retrieval and Generation (The Live Phase)
&lt;/h3&gt;

&lt;p&gt;This is what happens every time a user asks a question.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;User Query:&lt;/strong&gt; The user submits a query (e.g., "What is our policy on remote work?").&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Embed Query:&lt;/strong&gt; The user's query is also converted into a vector embedding using the same model from the indexing phase.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Search:&lt;/strong&gt; The vector store is searched to find the text chunks whose embeddings are most similar to the query's embedding. These are the most relevant pieces of information from your knowledge base.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Augment:&lt;/strong&gt; The original query and the retrieved text chunks are combined into a single, enriched prompt.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Generate:&lt;/strong&gt; This augmented prompt is sent to the LLM, which then generates a final answer based on the provided context.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conceptual Python Example
&lt;/h3&gt;

&lt;p&gt;Here’s a simplified Python-like pseudocode to illustrate the live phase:&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;# Assume we have these pre-configured components:
# - vector_store: A database of our indexed document chunks
# - embedding_model: A model to convert text to vectors
# - llm: A large language model for generation
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;answer_question_with_rag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;
    Answers a user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s query using the RAG process.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Embed the user's query
&lt;/span&gt;    &lt;span class="n"&gt;query_embedding&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;embedding_model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Retrieve relevant context from the vector store
&lt;/span&gt;    &lt;span class="n"&gt;relevant_chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;vector_store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_similar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query_embedding&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Augment the prompt
&lt;/span&gt;    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;relevant_chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;augmented_prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Based on the following context, please answer the user&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s query.
    If the context does not contain the answer, say so.

    Context:
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;

    Query:
    &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="si"&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;# 4. Generate the final answer
&lt;/span&gt;    &lt;span class="n"&gt;final_answer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;augmented_prompt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;final_answer&lt;/span&gt;

&lt;span class="c1"&gt;# Example usage:
&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What is our policy on remote work?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;answer_question_with_rag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to Use RAG
&lt;/h2&gt;

&lt;p&gt;RAG is incredibly powerful for specific use cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Customer Support Chatbots:&lt;/strong&gt; Provide answers based on product manuals, FAQs, and historical support tickets.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Internal Knowledge Bases:&lt;/strong&gt; Allow employees to ask questions about company policies, technical documentation, or project histories.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Personalized Content Recommendation:&lt;/strong&gt; Recommend articles or products based on a user's query and a catalog of items.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Educational Tools:&lt;/strong&gt; Create "ask the book" or "ask the lecture" applications where students can query course materials.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When &lt;em&gt;Not&lt;/em&gt; to Use RAG
&lt;/h2&gt;

&lt;p&gt;RAG is not a silver bullet. There are times when it's unnecessary or might even be counterproductive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Highly Creative or Open-Ended Tasks:&lt;/strong&gt; If you want the LLM to write a poem, a fictional story, or brainstorm ideas without constraints, feeding it specific documents is unnecessary.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;General Knowledge Questions:&lt;/strong&gt; For questions like "What is the capital of France?", the LLM's internal knowledge is more than sufficient and much faster to access.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;When Latency is Extremely Critical:&lt;/strong&gt; The retrieval step adds a small amount of latency. If you need an answer in milliseconds and the query is simple, a direct LLM call might be better.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Simple Command-and-Control:&lt;/strong&gt; For tasks like "turn on the lights" or "play music," a simpler NLU (Natural Language Understanding) system is more appropriate than a full RAG pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By understanding its strengths and limitations, you can leverage RAG to build more accurate, reliable, and useful AI-powered applications.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>vectordatabase</category>
      <category>python</category>
      <category>beginner</category>
    </item>
    <item>
      <title>Life Update #1</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Mon, 08 Dec 2025 14:07:04 +0000</pubDate>
      <link>https://forem.com/ctrix/life-update-1-30fg</link>
      <guid>https://forem.com/ctrix/life-update-1-30fg</guid>
      <description>&lt;p&gt;It feels like I'm navigating through a thick fog with no compass. I am really feeling lost, unworthy, and like I can't win. Every day feels like a battle, and lately, I've been losing every single one.&lt;/p&gt;



&lt;p&gt;A significant part of this weight comes from my career, or the lack thereof. I have been searching for a job for quite a while now, and the process has been soul-crushing. I pour hours into applications, tailoring my resume, and writing cover letters, only to be met with silence or the cold sting of automated rejection emails. It makes me question everything I thought I was good at. I have these skills, this experience, but it feels like none of it matters. I hate that I cannot find a decent job for what I can do.&lt;/p&gt;



&lt;p&gt;This professional struggle bleeds into everything else. Neither is life going well. It feels like I'm stuck in place while the world moves on around me. The frustration and disappointment have curdled into a sharp self-loathing. I just very, very dearly hate myself now. It's a constant, nagging voice in my head that tells me I'm not good enough, that I'm a failure.&lt;/p&gt;



&lt;p&gt;And in the midst of this turmoil, my heart aches with a grief that feels as fresh as ever. What specially bothers me is my mom. She appears in my dreams, vivid and real. For a fleeting moment, she's there, and everything feels okay. Then I wake up, and the emptiness rushes back in, a painful reminder of what I've lost. I miss her the most. Her absence is a void that nothing can fill.&lt;/p&gt;



&lt;p&gt;These dreams make me think about my life and my role in it. I wish I was a good enough son. I carry this heavy feeling that I've let her down, that I haven't lived up to the hopes she had for me.&lt;/p&gt;



&lt;p&gt;I feel like I've failed at everything—my career, my personal life, my duties as a son. The path forward is completely obscured. I am not sure what I should even do now. The weight of it all is immense.&lt;/p&gt;



&lt;p&gt;Even writing this fills my eyes with tears. But maybe, just maybe, putting it into words is a start.&lt;/p&gt;

</description>
      <category>career</category>
    </item>
    <item>
      <title>From npm to uv: A Node.js Developer's Guide to Python Project Setup</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Fri, 05 Dec 2025 08:26:36 +0000</pubDate>
      <link>https://forem.com/ctrix/from-npm-to-uv-a-nodejs-developers-guide-to-python-project-setup-ik5</link>
      <guid>https://forem.com/ctrix/from-npm-to-uv-a-nodejs-developers-guide-to-python-project-setup-ik5</guid>
      <description>&lt;p&gt;If you're a Node.js developer stepping into the Python world, you're probably used to the speed and simplicity of tools like &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;pnpm&lt;/code&gt;, or &lt;code&gt;yarn&lt;/code&gt;. You run &lt;code&gt;npm install&lt;/code&gt;, your dependencies pop into &lt;code&gt;node_modules&lt;/code&gt;, and you're off to the races. Then you encounter Python and hear about &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, &lt;code&gt;virtualenv&lt;/code&gt;, &lt;code&gt;pipenv&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt;... and it all feels a bit... fragmented.&lt;/p&gt;

&lt;p&gt;What if I told you there's a tool that brings a familiar, unified, and &lt;em&gt;blazingly fast&lt;/em&gt; experience to Python, much like the modern tools in the JavaScript ecosystem?&lt;/p&gt;

&lt;p&gt;Enter &lt;code&gt;uv&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is &lt;code&gt;uv&lt;/code&gt;? The &lt;code&gt;npm&lt;/code&gt; for Python You've Been Wishing For
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; is an extremely fast Python package installer and resolver, written in Rust by the same folks behind the &lt;code&gt;ruff&lt;/code&gt; linter. Think of it as a single binary that aims to replace &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;pip-tools&lt;/code&gt;, and &lt;code&gt;venv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a Node.js developer, the pitch is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's Fast:&lt;/strong&gt; Like, &lt;code&gt;esbuild&lt;/code&gt; or &lt;code&gt;swc&lt;/code&gt; fast. It uses a global cache and parallel processing to make dependency installation significantly quicker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's an All-in-One Tool:&lt;/strong&gt; It handles installing packages, running commands, and managing virtual environments. No more juggling different tools. It's like &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;npx&lt;/code&gt; rolled into one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It's Familiar:&lt;/strong&gt; The concepts map almost directly to what you already know from &lt;code&gt;npm&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 1: Installing &lt;code&gt;uv&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;First things first, you need Python installed. Once that's sorted, installing &lt;code&gt;uv&lt;/code&gt; is a one-liner.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
curl &lt;span class="nt"&gt;-LsSf&lt;/span&gt; https://astral.sh/uv/install.sh | sh

&lt;span class="c"&gt;# Windows&lt;/span&gt;
powershell &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"irm https://astral.sh/uv/install.ps1 | iex"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single binary is all you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Creating a Virtual Environment (Your New &lt;code&gt;node_modules&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In Node.js, dependencies are installed locally into a &lt;code&gt;node_modules&lt;/code&gt; directory by default. This is great for project isolation. Python's answer to this is the "virtual environment," which is essentially a folder containing a copy of the Python interpreter and all the packages for a specific project.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;uv&lt;/code&gt;, creating one is trivial.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a project and move into it&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-python-project
&lt;span class="nb"&gt;cd &lt;/span&gt;my-python-project

&lt;span class="c"&gt;# Create a virtual environment named .venv&lt;/span&gt;
uv venv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a &lt;code&gt;.venv&lt;/code&gt; folder. &lt;strong&gt;Think of &lt;code&gt;.venv&lt;/code&gt; as your new &lt;code&gt;node_modules&lt;/code&gt; directory.&lt;/strong&gt; It's where all your project-specific Python packages will live, keeping your global space clean.&lt;/p&gt;

&lt;p&gt;Just like &lt;code&gt;.gitignore&lt;/code&gt; includes &lt;code&gt;node_modules&lt;/code&gt;, you should add &lt;code&gt;.venv&lt;/code&gt; to your &lt;code&gt;.gitignore&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Activating the Environment
&lt;/h2&gt;

&lt;p&gt;This is one small step that's different from the Node.js workflow. To use the tools and packages inside your virtual environment, you need to "activate" it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
&lt;span class="nb"&gt;source&lt;/span&gt; .venv/bin/activate

&lt;span class="c"&gt;# Windows (Command Prompt)&lt;/span&gt;
.venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\a&lt;/span&gt;ctivate.bat

&lt;span class="c"&gt;# Windows (PowerShell)&lt;/span&gt;
.venv&lt;span class="se"&gt;\S&lt;/span&gt;cripts&lt;span class="se"&gt;\A&lt;/span&gt;ctivate.ps1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your terminal prompt will change to indicate that you're now "inside" the virtual environment. Now, any &lt;code&gt;python&lt;/code&gt; or &lt;code&gt;pip&lt;/code&gt; command will use the versions inside &lt;code&gt;.venv&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Installing Packages (The &lt;code&gt;npm install&lt;/code&gt; Moment)
&lt;/h2&gt;

&lt;p&gt;Ready for that familiar &lt;code&gt;npm install&lt;/code&gt; feeling? Let's install a package. The &lt;code&gt;requests&lt;/code&gt; library is the &lt;code&gt;axios&lt;/code&gt; of the Python world, so we'll start there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This is like `npm install requests`&lt;/span&gt;
uv pip &lt;span class="nb"&gt;install &lt;/span&gt;requests
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see &lt;code&gt;uv&lt;/code&gt; resolve and install the package with incredible speed. If you inspect the &lt;code&gt;.venv&lt;/code&gt; directory, you'll find &lt;code&gt;requests&lt;/code&gt; and its dependencies in there, just like you'd find packages in &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To install a dev dependency, you can add it to a specific requirements file, similar to &lt;code&gt;devDependencies&lt;/code&gt;. For now, let's stick to the basics.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5: Saving Dependencies (&lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;requirements.txt&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;In Node.js, your dependencies are automatically saved to &lt;code&gt;package.json&lt;/code&gt;. &lt;code&gt;uv&lt;/code&gt; follows the classic Python pattern of using a &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;You can generate this file after installing your packages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This is like creating a snapshot of your dependencies&lt;/span&gt;
uv pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;requirements.txt&lt;/code&gt; will now look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# requirements.txt
certifi==2023.11.17
charset-normalizer==3.3.2
idna==3.6
requests==2.31.0
urllib3==2.1.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This file is your &lt;code&gt;package.json&lt;/code&gt;'s &lt;code&gt;dependencies&lt;/code&gt; list. Anyone else on your team can now install the exact same dependencies using this file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This is like running `npm install` after cloning a repo&lt;/span&gt;
uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 6: Running Tools with &lt;code&gt;uvx&lt;/code&gt; (The &lt;code&gt;npx&lt;/code&gt; Equivalent)
&lt;/h2&gt;

&lt;p&gt;One of the best parts of the Node.js ecosystem is &lt;code&gt;npx&lt;/code&gt;, which lets you run a package's binary without installing it globally.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;uv&lt;/code&gt; has a direct equivalent: &lt;code&gt;uvx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's try it with &lt;code&gt;cowsay&lt;/code&gt;, a classic command-line toy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This is like `npx cowsay "Hello from uv!"`&lt;/span&gt;
uvx cowsay &lt;span class="s2"&gt;"Hello from uv!"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;uvx&lt;/code&gt; will temporarily install &lt;code&gt;cowsay&lt;/code&gt;, run the command, and then clean it up. It's perfect for running project-based tools like linters (&lt;code&gt;ruff&lt;/code&gt;), formatters (&lt;code&gt;black&lt;/code&gt;), or web servers (&lt;code&gt;uvicorn&lt;/code&gt;) without polluting your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tying It All Together: A Node.js to &lt;code&gt;uv&lt;/code&gt; Cheat Sheet
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Node.js (&lt;code&gt;npm&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Python (&lt;code&gt;uv&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uv pip install &amp;lt;pkg&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installs a package to the local environment.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm install&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uv pip install -r requirements.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Installs all dependencies from the project file.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;requirements.txt&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The file that lists your project's dependencies.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node_modules/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.venv/&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The local, isolated folder containing dependencies.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npx &amp;lt;cmd&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uvx &amp;lt;cmd&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Executes a package command without a permanent install.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm init -y&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;uv venv&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initializes a new project environment.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;The Python ecosystem is powerful, but for a Node.js developer, the tooling can sometimes feel like a barrier. &lt;code&gt;uv&lt;/code&gt; changes that. By providing a single, cohesive, and lightning-fast tool, it offers a developer experience that feels right at home.&lt;/p&gt;

&lt;p&gt;So next time you start a Python project, give &lt;code&gt;uv&lt;/code&gt; a try. You might just find the &lt;code&gt;npm&lt;/code&gt;-like workflow you've been looking for.&lt;/p&gt;

</description>
      <category>python</category>
      <category>webdev</category>
      <category>beginner</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Explicit is Better Than Implicit: Mastering Pytest Fixtures and Async Testing</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Wed, 03 Dec 2025 09:03:02 +0000</pubDate>
      <link>https://forem.com/ctrix/explicit-is-better-than-implicit-mastering-pytest-fixtures-and-async-testing-k78</link>
      <guid>https://forem.com/ctrix/explicit-is-better-than-implicit-mastering-pytest-fixtures-and-async-testing-k78</guid>
      <description>&lt;p&gt;Look, we need to talk about async testing in Python. If you've ever stared at a &lt;code&gt;ModuleNotFoundError: No module named 'trio'&lt;/code&gt; when you're just trying to test your FastAPI endpoints, you know the pain. You didn't ask for trio. You don't even know what trio is. You just want your tests to pass.&lt;/p&gt;

&lt;p&gt;Let's fix this properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem: Event Loops Are Confusing
&lt;/h2&gt;

&lt;p&gt;Here's the thing about async Python: your &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;await&lt;/code&gt; code needs an event loop to run. That's not optional. And there are multiple event loop implementations out there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;asyncio&lt;/strong&gt; - Python's built-in standard library solution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;trio&lt;/strong&gt; - Third-party library with structured concurrency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;curio&lt;/strong&gt; - Another third-party option (less common these days)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you're testing async code with pytest, you need something to manage these event loops. Enter &lt;code&gt;pytest-anyio&lt;/code&gt; - a plugin that's supposed to make your life easier. Spoiler alert: it doesn't always.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Are Pytest Fixtures?
&lt;/h2&gt;

&lt;p&gt;Before we dive into the async madness, let's talk about fixtures. If you've used pytest, you've probably seen them. If you haven't understood them, you're not alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fixtures are just functions that set up stuff for your tests.&lt;/strong&gt; That's it. They run before your test, give your test what it needs, and clean up after. Think of them as the responsible adult in the room.&lt;/p&gt;

&lt;p&gt;Here's what makes them powerful:&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency Injection (The Good Kind)
&lt;/h3&gt;

&lt;p&gt;You want a database connection? Just ask for it:&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_something&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;database_connection&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# pytest automatically gives you the connection
&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;database_connection&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No manual setup. No global variables. Just declare what you need, and pytest handles it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup and Teardown Without the Boilerplate
&lt;/h3&gt;

&lt;p&gt;Remember the old &lt;code&gt;setUp()&lt;/code&gt; and &lt;code&gt;tearDown()&lt;/code&gt; methods? Fixtures do this better:&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="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_test_tables&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# Setup: runs before your test
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;yield&lt;/span&gt;  &lt;span class="c1"&gt;# Your test runs here
&lt;/span&gt;
    &lt;span class="c1"&gt;# Teardown: runs after your test
&lt;/span&gt;    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything before &lt;code&gt;yield&lt;/code&gt; is setup. Everything after is cleanup. Your test gets a clean database every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scopes: Because Not Everything Needs to Run Every Time
&lt;/h3&gt;

&lt;p&gt;Fixtures have scopes that control how often they run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;function&lt;/code&gt; (default) - Runs for every single test&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;class&lt;/code&gt; - Runs once per test class&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;module&lt;/code&gt; - Runs once per test file&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;session&lt;/code&gt; - Runs once for your entire test suite&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This matters for performance. Creating a database connection once per session? Smart. Creating it 500 times for 500 tests? Not smart.&lt;/p&gt;

&lt;h2&gt;
  
  
  The anyio Backend Problem
&lt;/h2&gt;

&lt;p&gt;Now here's where things get spicy. When you use &lt;code&gt;pytest-anyio&lt;/code&gt; to test async code, it needs to know which event loop to use. By default, it tries to be helpful and test against &lt;strong&gt;all available backends&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Sounds great, right? Wrong.&lt;/p&gt;

&lt;p&gt;Here's what actually happens:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You write tests using &lt;code&gt;asyncio&lt;/code&gt; (because that's what FastAPI uses)&lt;/li&gt;
&lt;li&gt;You install &lt;code&gt;pytest-anyio&lt;/code&gt; to test your async code&lt;/li&gt;
&lt;li&gt;pytest-anyio sees you have &lt;code&gt;trio&lt;/code&gt; installed (maybe from another project)&lt;/li&gt;
&lt;li&gt;It tries to run your tests with trio&lt;/li&gt;
&lt;li&gt;Your &lt;code&gt;aiosqlite&lt;/code&gt; database driver explodes because it only works with asyncio&lt;/li&gt;
&lt;li&gt;You get errors like &lt;code&gt;ModuleNotFoundError: No module named 'trio'&lt;/code&gt; or &lt;code&gt;RuntimeError: no running event loop&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You question your career choices&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Solution: Be Explicit
&lt;/h2&gt;

&lt;p&gt;Stop letting pytest-anyio guess. Tell it exactly what you want.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create an anyio_backend Fixture
&lt;/h3&gt;

&lt;p&gt;In your &lt;code&gt;tests/conftest.py&lt;/code&gt;, add this:&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;pytest&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&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;anyio_backend&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Use asyncio for all async tests. Period.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asyncio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. This fixture runs once per test session and tells pytest-anyio: "Use asyncio. Don't try to be clever."&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Make Sure trio Isn't Interfering
&lt;/h3&gt;

&lt;p&gt;If you don't need trio, uninstall it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip uninstall trio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Mark Your Async Tests Properly
&lt;/h3&gt;

&lt;p&gt;When you write async tests, mark them explicitly:&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="nd"&gt;@pytest.mark.anyio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;asyncio&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_and_get_example&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;create_test_tables&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Your test code here
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;backend='asyncio'&lt;/code&gt; parameter is redundant if you have the fixture, but it makes your intent crystal clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real-World Example: Testing FastAPI with SQLModel
&lt;/h2&gt;

&lt;p&gt;Let's put this all together with a real example. You've got a FastAPI app with a database, and you want to test it properly.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Database Setup Fixture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/conftest.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sqlmodel&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLModel&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tests.test_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;session&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;anyio_backend&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;asyncio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_test_tables&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;
    Creates tables before each test, drops them after.
    Each test gets a clean database.
    &lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;create_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;yield&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;begin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run_sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SQLModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;drop_all&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Test Client Fixture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# tests/features/test_examples_api.py
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;pytest&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi.testclient&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestClient&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src.app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src.features.examples.api&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;get_example_service&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;src.features.examples.service&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ExampleService&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;tests.test_db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestingSessionLocal&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;override_get_example_service&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Override the dependency to use test database&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestingSessionLocal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nc"&gt;ExampleService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dependency_overrides&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;get_example_service&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;override_get_example_service&lt;/span&gt;

&lt;span class="nd"&gt;@pytest.fixture&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scope&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;module&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;client&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;TestClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Actual Test
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@pytest.mark.anyio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backend&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;asyncio&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_create_and_get_example&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;TestClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;create_test_tables&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Create an example
&lt;/span&gt;    &lt;span class="n"&gt;create_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&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/v1/examples/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;json&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;name&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 Example&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;description&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;A test description&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;create_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="n"&gt;created_example&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;create_response&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;created_example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test Example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;created_example&lt;/span&gt;

    &lt;span class="c1"&gt;# Fetch it back
&lt;/span&gt;    &lt;span class="n"&gt;example_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;created_example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;get_response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/api/v1/examples/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;example_id&lt;/span&gt;&lt;span class="si"&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;assert&lt;/span&gt; &lt;span class="n"&gt;get_response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;
    &lt;span class="n"&gt;fetched_example&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_response&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="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fetched_example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;example_id&lt;/span&gt;
    &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;fetched_example&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Test Example&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the test just declares what it needs (&lt;code&gt;client&lt;/code&gt;, &lt;code&gt;create_test_tables&lt;/code&gt;), and pytest handles the rest. Clean database, test client, all set up and torn down automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Fixed (The Troubleshooting Story)
&lt;/h2&gt;

&lt;p&gt;When I first set this up, tests were failing with trio errors. Here's what was wrong and how we fixed it:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem 1: pytest-anyio was trying to use trio&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Created the &lt;code&gt;anyio_backend&lt;/code&gt; fixture returning &lt;code&gt;"asyncio"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem 2: trio was installed but incompatible with aiosqlite&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Uninstalled trio since we don't need it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Problem 3: Conflicting configurations in pyproject.toml and pytest.ini&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution&lt;/strong&gt;: Removed old anyio configurations, kept it simple&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson? &lt;strong&gt;Explicit is better than implicit.&lt;/strong&gt; Don't let your test framework guess what you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fixtures are dependency injection for tests&lt;/strong&gt; - Declare what you need, pytest provides it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;yield&lt;/code&gt; for setup/teardown&lt;/strong&gt; - Code before yield is setup, code after is cleanup&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose the right scope&lt;/strong&gt; - Don't recreate expensive resources for every test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be explicit about your async backend&lt;/strong&gt; - Create an &lt;code&gt;anyio_backend&lt;/code&gt; fixture that returns &lt;code&gt;"asyncio"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Don't install backends you don't use&lt;/strong&gt; - If you're using asyncio, you probably don't need trio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mark async tests properly&lt;/strong&gt; - Use &lt;code&gt;@pytest.mark.anyio(backend='asyncio')&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Bottom Line
&lt;/h2&gt;

&lt;p&gt;Async testing in Python doesn't have to be painful. The key is understanding what's actually happening:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your async code needs an event loop&lt;/li&gt;
&lt;li&gt;pytest-anyio manages that loop for you&lt;/li&gt;
&lt;li&gt;You need to tell it which loop to use&lt;/li&gt;
&lt;li&gt;Fixtures handle setup and teardown automatically&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get these pieces right, and your tests will be reliable, fast, and maintainable. Get them wrong, and you'll be debugging event loop errors at 2 AM.&lt;/p&gt;

&lt;p&gt;Choose wisely.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have you dealt with async testing nightmares? What tripped you up? Let me know in the comments or hit me up on Twitter. And if this saved you from a debugging session, you're welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>pytest</category>
      <category>beginners</category>
      <category>ai</category>
    </item>
    <item>
      <title>A Developer's Guide to Jest: What, When, and How to Test</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Fri, 28 Nov 2025 07:56:52 +0000</pubDate>
      <link>https://forem.com/ctrix/a-developers-guide-to-jest-what-when-and-how-to-test-39pe</link>
      <guid>https://forem.com/ctrix/a-developers-guide-to-jest-what-when-and-how-to-test-39pe</guid>
      <description>&lt;p&gt;Jest is a delightful JavaScript Testing Framework with a focus on simplicity. It's one of the most popular choices in the JavaScript ecosystem, especially within the React community, but its power and versatility make it a great fit for nearly any JavaScript project. This guide will walk you through how to use Jest, what to test with it, and when you might need to reach for a different tool.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started with Jest
&lt;/h3&gt;

&lt;p&gt;Setting up Jest is incredibly straightforward. In a new or existing project, you can add it with your favorite package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Using npm&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--save-dev&lt;/span&gt; jest

&lt;span class="c"&gt;# Using pnpm&lt;/span&gt;
pnpm add &lt;span class="nt"&gt;-D&lt;/span&gt; jest

&lt;span class="c"&gt;# Using yarn&lt;/span&gt;
yarn add &lt;span class="nt"&gt;--dev&lt;/span&gt; jest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add a test script to your &lt;code&gt;package.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="nl"&gt;"scripts"&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;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest"&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;Now, let's write a simple test. Imagine you have a utility function that adds two numbers.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sum.js&lt;/code&gt;&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;b&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;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To test this, you would create a file named &lt;code&gt;sum.test.js&lt;/code&gt;. Jest automatically discovers and runs files with &lt;code&gt;.test.js&lt;/code&gt; or &lt;code&gt;.spec.js&lt;/code&gt; extensions.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sum.test.js&lt;/code&gt;&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./sum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should add two numbers correctly&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should handle negative numbers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="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="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;When you run &lt;code&gt;pnpm test&lt;/code&gt;, Jest will execute this file and give you a clean, readable output indicating whether your tests passed or failed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Concepts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;describe(name, fn)&lt;/code&gt;: Creates a block that groups together several related tests.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;it(name, fn)&lt;/code&gt; or &lt;code&gt;test(name, fn)&lt;/code&gt;: This is your actual test case.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;expect(value)&lt;/code&gt;: The starting point for any assertion. When you wrap a value in &lt;code&gt;expect&lt;/code&gt;, you get access to "matchers."&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Matchers&lt;/strong&gt;: Functions that let you validate a value in different ways. In the example above, &lt;code&gt;.toBe(value)&lt;/code&gt; is a matcher that checks for exact equality. Other common matchers include &lt;code&gt;toEqual&lt;/code&gt; (for deep object equality), &lt;code&gt;toBeTruthy&lt;/code&gt;, &lt;code&gt;toBeFalsy&lt;/code&gt;, &lt;code&gt;toContain&lt;/code&gt;, and many more.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What to Test with Jest
&lt;/h3&gt;

&lt;p&gt;Jest excels at &lt;strong&gt;unit testing&lt;/strong&gt; and &lt;strong&gt;integration testing&lt;/strong&gt; in a Node.js environment. Here are the ideal candidates for Jest tests:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Pure Functions &amp;amp; Business Logic&lt;/strong&gt;: This is Jest's sweet spot. Functions that take inputs and produce outputs without side effects (like API calls or file system changes) are easy to test and form the backbone of a reliable application.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Component Logic&lt;/strong&gt;: For UI frameworks like React, Vue, or Svelte, you can test the logic inside your components. For example, you can check if a component's state updates correctly when a function is called, without actually rendering the component to a screen.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;API Endpoints&lt;/strong&gt;: In a Node.js backend (like Express or Fastify), you can test your API endpoints by mocking requests and asserting that the correct status codes, headers, and JSON payloads are returned.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What &lt;em&gt;Not&lt;/em&gt; to Test with Jest
&lt;/h3&gt;

&lt;p&gt;It's equally important to know when Jest isn't the right tool for the job.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;End-to-End (E2E) Testing&lt;/strong&gt;: E2E testing involves simulating a real user's journey through your application in a browser. Since Jest runs in a Node.js environment (via JSDOM), it doesn't have a real browser engine. It cannot click buttons, fill out forms, and navigate pages as a user would.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Use Instead&lt;/strong&gt;: Tools like &lt;strong&gt;Cypress&lt;/strong&gt; or &lt;strong&gt;Playwright&lt;/strong&gt; are designed specifically for this. They run tests in actual browsers (Chrome, Firefox, etc.), providing much more reliable results for user-facing flows.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Visual Regression Testing&lt;/strong&gt;: This is the practice of taking screenshots of your UI and comparing them against a baseline to catch unintended visual changes. While Jest's snapshot testing can capture the &lt;em&gt;structure&lt;/em&gt; of a component's output, it knows nothing about its visual appearance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Use Instead&lt;/strong&gt;: Tools like &lt;strong&gt;Percy&lt;/strong&gt; or &lt;strong&gt;Chromatic&lt;/strong&gt; integrate with E2E or component testing frameworks to perform robust visual testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance Testing&lt;/strong&gt;: Jest is not built for benchmarking code performance. Its test runner overhead and focus on correctness make it unsuitable for measuring execution speed accurately.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Use Instead&lt;/strong&gt;: Use dedicated benchmarking libraries like &lt;strong&gt;Benchmark.js&lt;/strong&gt; or profiling tools built into Node.js and browsers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Advanced Features: Mocking and Snapshot Testing
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Mocking&lt;/strong&gt;: Jest has a powerful built-in mocking system (&lt;code&gt;jest.fn()&lt;/code&gt;, &lt;code&gt;jest.spyOn()&lt;/code&gt;, &lt;code&gt;jest.mock()&lt;/code&gt;). Mocking allows you to replace dependencies (like an API service or a database module) with a "fake" version. This lets you test a piece of code in isolation, ensuring you're only testing its logic and not that of its dependencies.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Snapshot Testing&lt;/strong&gt;: When you run a snapshot test, Jest generates a &lt;code&gt;.snap&lt;/code&gt; file containing the rendered output of a component or the structure of an object. On subsequent test runs, Jest compares the new output to the saved snapshot. If they don't match, the test fails. This is useful for ensuring that UI component structures or large object shapes don't change unexpectedly. However, use it with caution—it's easy to accidentally approve incorrect snapshots.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Jest is a phenomenal tool for building a fast, reliable, and maintainable testing suite for your JavaScript codebase. It is the ideal choice for unit and integration tests, where you are verifying the logic and correctness of your functions, modules, and components.&lt;/p&gt;

&lt;p&gt;However, a comprehensive testing strategy doesn't stop there. For verifying user journeys and visual presentation, you should complement Jest with specialized tools like Cypress, Playwright, or Percy. By using the right tool for the right job, you can build confidence in your code and ship better products.&lt;/p&gt;

</description>
      <category>jest</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Python Concurrency: A Guide to Threads, Processes, and Asyncio</title>
      <dc:creator>Chandrashekhar Kachawa</dc:creator>
      <pubDate>Wed, 26 Nov 2025 05:35:47 +0000</pubDate>
      <link>https://forem.com/ctrix/python-concurrency-a-guide-to-threads-processes-and-asyncio-45n4</link>
      <guid>https://forem.com/ctrix/python-concurrency-a-guide-to-threads-processes-and-asyncio-45n4</guid>
      <description>&lt;p&gt;Your Python script needs to do multiple things at once to be faster. But how? Python offers a rich but sometimes confusing landscape of concurrency and parallelism tools. Should you use threads, processes, or asyncio?&lt;/p&gt;

&lt;p&gt;Choosing the right tool for the job is the key to writing efficient, scalable code. This guide will walk you through the three main concurrency models in Python, explaining what they are, how to use them, and when to choose each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Foundation: &lt;code&gt;concurrent.futures&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;For traditional, blocking code, Python's &lt;code&gt;concurrent.futures&lt;/code&gt; module provides a beautiful, high-level API for managing pools of threads and processes. It introduces the "Executor" pattern, where you submit jobs to a pool and retrieve the results.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;ThreadPoolExecutor&lt;/code&gt;: For I/O-Bound Work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When to use it:&lt;/strong&gt; When your task spends most of its time &lt;strong&gt;waiting&lt;/strong&gt; for external resources. This is called I/O-bound work. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Making network requests (e.g., calling APIs, scraping websites).&lt;/li&gt;
&lt;li&gt;Reading or writing from a slow disk.&lt;/li&gt;
&lt;li&gt;Querying a database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Due to Python's Global Interpreter Lock (GIL), threads are not suitable for speeding up CPU-heavy tasks, but they are perfect for I/O-bound tasks because they can "wait" concurrently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax Example:&lt;/strong&gt; Let's download the content of several web pages.&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;requests&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ThreadPoolExecutor&lt;/span&gt;

&lt;span class="n"&gt;URLS&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;https://www.python.org/&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;https://www.djangoproject.com/&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;https://flask.palletsprojects.com/&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="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;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="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fetching &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;requests&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="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Fetched &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; with status &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="si"&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;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ThreadPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_workers&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# The map function runs `fetch_url` for each item in URLS
&lt;/span&gt;    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;URLS&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;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;URLS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;URL: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, Length: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The thread pool allows all three &lt;code&gt;requests.get()&lt;/code&gt; calls to happen concurrently, dramatically speeding up the total execution time.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;ProcessPoolExecutor&lt;/code&gt;: For CPU-Bound Work
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When to use it:&lt;/strong&gt; When your task is doing heavy computation and maxing out a CPU core. This is called CPU-bound work. Examples include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Complex mathematical calculations.&lt;/li&gt;
&lt;li&gt;Image or video processing.&lt;/li&gt;
&lt;li&gt;Data analysis on large datasets.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Processes run in separate memory spaces and have their own Python interpreter, which allows them to bypass the GIL and run on different CPU cores in true parallel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax Example:&lt;/strong&gt; Let's perform a heavy calculation on a list of numbers.&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;from&lt;/span&gt; &lt;span class="n"&gt;concurrent.futures&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ProcessPoolExecutor&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;heavy_calculation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calculating for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="si"&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="c1"&gt;# A silly, CPU-intensive task
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="n"&gt;numbers_to_process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;150_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200_000&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nc"&gt;ProcessPoolExecutor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;executor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;heavy_calculation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;numbers_to_process&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;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;numbers_to_process&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Calculation for &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; returned &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the syntax is nearly identical to the thread pool example! &lt;code&gt;concurrent.futures&lt;/code&gt; makes it easy to switch between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Third Model: &lt;code&gt;asyncio&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;What if your code is already &lt;code&gt;async&lt;/code&gt; and uses libraries like &lt;code&gt;httpx&lt;/code&gt; or &lt;code&gt;asyncpg&lt;/code&gt;? In this case, you don't need threads or processes. &lt;code&gt;asyncio&lt;/code&gt; has its own, more efficient tools for managing I/O-bound concurrency.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managing Async Tasks: &lt;code&gt;gather&lt;/code&gt; and &lt;code&gt;TaskGroup&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Let's say we have a simple &lt;code&gt;async&lt;/code&gt; function:&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;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;what&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="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;what&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;asyncio.gather&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;gather&lt;/code&gt; is a high-level utility to run multiple awaitable objects concurrently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gathering Coroutines (most common):&lt;/strong&gt;&lt;br&gt;
You can pass coroutine objects directly to &lt;code&gt;gather&lt;/code&gt;. It will automatically schedule them as tasks.&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;asyncio&lt;/span&gt;

&lt;span class="c1"&gt;# This runs both `say_after` calls concurrently
&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;world&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Gathering Tasks:&lt;/strong&gt;&lt;br&gt;
You can also create tasks explicitly with &lt;code&gt;asyncio.create_task&lt;/code&gt; and then gather them. This gives you more control if you need to interact with the &lt;code&gt;Task&lt;/code&gt; objects before they are complete.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;task1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;task2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gather&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;code&gt;asyncio.TaskGroup&lt;/code&gt; (The Modern, Safe Way)
&lt;/h4&gt;

&lt;p&gt;Introduced in Python 3.11, &lt;code&gt;TaskGroup&lt;/code&gt; is a modern and safer way to manage concurrent tasks. It uses a context manager (&lt;code&gt;async with&lt;/code&gt;) to create a scope, guaranteeing that all tasks created within it are awaited before the block is exited.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Structured Concurrency:&lt;/strong&gt; No more "forgotten" tasks that run in the background.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Superior Exception Handling:&lt;/strong&gt; If any task in the group fails, all other tasks are automatically cancelled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Syntax Example:&lt;/strong&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="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;TaskGroup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;tg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;tg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;tg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;say_after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;world&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Both tasks have now completed.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For new &lt;code&gt;asyncio&lt;/code&gt; code, &lt;code&gt;TaskGroup&lt;/code&gt; is generally preferred over &lt;code&gt;gather&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary: When to Use Which?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Is your task...&lt;/th&gt;
&lt;th&gt;And are you using...&lt;/th&gt;
&lt;th&gt;Your best tool is...&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CPU-Bound&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any Python code&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ProcessPoolExecutor&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I/O-Bound&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blocking libraries (e.g., &lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;psycopg2&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ThreadPoolExecutor&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;I/O-Bound&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;async&lt;/code&gt; libraries (e.g., &lt;code&gt;httpx&lt;/code&gt;, &lt;code&gt;asyncpg&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;asyncio&lt;/code&gt; (&lt;code&gt;TaskGroup&lt;/code&gt; or &lt;code&gt;gather&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Python provides powerful tools for every concurrency and parallelism need. The key is to correctly identify the nature of your task. By choosing the right model—Processes for CPU-bound work, Threads for blocking I/O, and Asyncio for non-blocking I/O—you can write efficient, scalable, and high-performance applications.&lt;/p&gt;

</description>
      <category>python</category>
      <category>beginner</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
