<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[subham]]></title><description><![CDATA[subham]]></description><link>https://blogs.saurabhsubham.in</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 10:00:30 GMT</lastBuildDate><atom:link href="https://blogs.saurabhsubham.in/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[JS Refactors at Scale? Meet Codemods + AST]]></title><description><![CDATA[We have all been there when we have to upgrade a library and there are lot of changes we need to do manually to each file. therefore some libraries provide codemods (like a script) that automatically updates the deprecated code required for the new v...]]></description><link>https://blogs.saurabhsubham.in/creating-your-own-codemod</link><guid isPermaLink="true">https://blogs.saurabhsubham.in/creating-your-own-codemod</guid><category><![CDATA[js]]></category><category><![CDATA[automation]]></category><category><![CDATA[Codemod cli]]></category><category><![CDATA[React]]></category><category><![CDATA[devtools]]></category><dc:creator><![CDATA[Subham Saurabh]]></dc:creator><pubDate>Sun, 18 May 2025 17:09:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/70Rir5vB96U/upload/bf982d2aef56cd60619be7e939ce4781.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We have all been there when we have to upgrade a library and there are lot of changes we need to do manually to each file. therefore some libraries provide codemods (like a script) that automatically updates the deprecated code required for the new version of that library. I know we can achieve this by node file system, but there is an issue where we have to specify directly what we want to change or copying the arguments of a function, but codemods are superfast as they run on multiple threads. Enough with the chit chat and let’s build one for ourself.</p>
<h2 id="heading-what-is-a-codemod">What is a “Codemod”?</h2>
<p>By doing a google search, <mark>Codemods</mark> are transformations that run on your codebase programmatically. This allows a large number of changes to be programmatically applied without having to manually go through every file.</p>
<p>Let’s simplify it. if we break the word <code>codemod</code> it kind of sounds like “code modification” or in short <code>codemod</code>. we are creating a script that goes through our file and modifies our code. It’s as simple as that.But you can thing why not regex, we can also target via regex and write a node script that does the same thing. Regex works great for text but not for code. Regex doesn’t understand structure. for e.g when we are updating some random component like say a <code>Button</code></p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">primary</span>&gt;</span>Click Me!<span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>

<span class="hljs-tag">&lt;<span class="hljs-name">Button</span> <span class="hljs-attr">variant</span>=<span class="hljs-string">"primary"</span>&gt;</span>Click Me!<span class="hljs-tag">&lt;/<span class="hljs-name">Button</span>&gt;</span>
</code></pre>
<p>and you want to transform <code>Button</code> <code>primary</code> attribute to <code>variant=”Primary”</code> <em>.</em> With the regex approach it will look something like this <code>code.replace(/&lt;Button\s+primary(.*?)&gt;/g, '&lt;Button variant="primary"$1&gt;')</code> , Now imagine you have to target <code>&lt;Button secondary&gt;Click Me!&lt;/Button&gt;</code> or <code>success</code>.It becomes very hard from regex to manage all this scenario. Now to handle these situations we are going to need to use <mark>AST</mark>(Abstract syntax tree). another jargon huh. Let’s understand AST</p>
<h2 id="heading-what-is-ast">What is AST?</h2>
<p>According to google `An Abstract Syntax Tree (AST) is <strong><mark>a tree representation of the abstract syntactic structure of a code snippet or formal language.</mark></strong> A lot of jargon and a little hard to understand. Lt’s simplify it. Think of an AST is like an x-ray of your code. It doesn’t care about how your code looks but focuses on what it’s mean.</p>
<p>For e.g <code>const sum = a + b</code> take this code now how it will look as an abstract tree</p>
<pre><code class="lang-javascript">VariableDeclaration (<span class="hljs-keyword">const</span>)
├── VariableDeclarator
│   ├── Identifier (sum)
│   └── BinaryExpression (+)
│       ├── Identifier (a)
│       └── Identifier (b)
</code></pre>
<p><em>This is the AST.</em></p>
<p>so instead of working with raw text (which is messy and error-prone), you get a clean, structured representation that’s much easier to traverse, analyze and modify safely.</p>
<p>If you want to visualize your code to AST you can go to this website <a target="_blank" href="https://astexplorer.net/">link</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747580564987/dc1546d9-029e-4394-9a1e-62c6b8592013.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-codemod-tools">Codemod Tools</h2>
<p>There are some popular codemod tools that helps us streamline the process.</p>
<ul>
<li><p>jscodeShift (by facebook): <a target="_blank" href="https://github.com/facebook/jscodeshift">https://github.com/facebook/jscodeshift</a></p>
</li>
<li><p>ts-morph (for TypeScript-heavy codebases) : <a target="_blank" href="https://ts-morph.com/">https://ts-morph.com/</a></p>
</li>
<li><p>babel plugins (for custom transformations) : <a target="_blank" href="https://babeljs.io/">https://babeljs.io/</a></p>
</li>
<li><p>recast and ast-types (used under the hood)</p>
</li>
</ul>
<p>For the purpose of this blog we’ll just focus on jscodeshift.</p>
<h2 id="heading-writing-your-first-codemod">Writing your first codemod</h2>
<p>Let’s start with the simple like renaming a variable. Here I’ll take the example of upgrading <code>react-router-dom</code> from <code>v5</code> top <code>v6</code></p>
<p>In <code>react-router-dom v6</code> there is no <code>useHistory</code> and we have to rename it to <code>useNavigate</code>.</p>
<p>First let’s install <code>jscodeShift</code> in our project where we want to change our code. Also you can go through the <code>jscodeShift</code> docs here <a target="_blank" href="https://jscodeshift.com/build/api-reference/">https://jscodeshift.com</a>.</p>
<pre><code class="lang-bash">npm install jscodeshift
</code></pre>
<p>Second create a javascript file in the root name <code>migrate.js</code>(you can name anything) and inside that create a module that exports a function and accepts two parameters <em>file</em> and <em>api.</em></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
}
</code></pre>
<p>Now we need to initialize the jscodeshift and file source inside the transformer function to convert the source code to AST.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
    <span class="hljs-keyword">const</span> j = api.jscodeshift;
    <span class="hljs-keyword">const</span> root = j(file.source);
}
</code></pre>
<p>let’s see what going on</p>
<ul>
<li><p><code>const j = api.jscodeshift;</code> we are assigning the <code>jscodeshift</code> library to the variable j as an utility function.We’ll use this for <em>parsing</em>, <em>traversing</em> and <em>transforming</em> <code>js</code> code as an <code>AST</code>.</p>
</li>
<li><p><code>const root = j(file.source);</code> It will parse the source code of the file into an <code>AST</code> and wraps it in <code>jscodeshift</code> collection. Now <code>root</code> is the entry point for finding and transforming nodes in the code.</p>
</li>
</ul>
<p>now we want to change the <code>const history = useHistory()</code> → <code>const navigate = useNavigate()</code>, now to do so we’ll visualize using this link <a target="_blank" href="https://astexplorer.net/">https://astexplorer.net/</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747583390850/2dd54adb-dbec-45d9-8d01-b231ee52f84d.png" alt class="image--center mx-auto" /></p>
<p>Now this is how this simple react program will look like.</p>
<p>Let’s deep dive the right part. we can see we have two <code>ImportDeclaration</code> as in the left we have two import for <code>react</code> and <code>react-router-dom</code>. Then there is a <code>VariableDeclaration</code> and that is <code>CustomComponent</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> CustomComponent = <span class="hljs-function">(<span class="hljs-params">props</span>)=&gt;</span>{
  <span class="hljs-keyword">const</span> history = useHistory();
  <span class="hljs-keyword">const</span> location = useLocation()

  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> history.push(`/hello/${e}`)}&gt;click me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747583694051/546491b6-e77a-4dfc-ae9a-74ecd1e5a2cc.png" alt class="image--center mx-auto" /></p>
<p>now if we expand the VariableDeclarator we get a lot of info, but if you can see the highlighted part we have two more <code>VariableDeclaration</code> and a <code>ReturnStatement</code>.Now if we see on the left part of the code, we’ll see</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> history = useHistory();
<span class="hljs-keyword">const</span> location = useLocation()

<span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{(e)</span> =&gt;</span> history.push(`/hello/${e}`)}&gt;click me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
</code></pre>
<p>these <code>history</code>, <code>location</code> and the <code>return</code> statements are the <code>VariableDeclaration</code> and the <code>ReturnStatement</code>.</p>
<p>Now we need to find the node that contains the <code>history</code> and <code>useHistory</code> and to do that jscodehift provides us a method called <code>find(type)</code> it takes what <em>type</em> of node you want to finds. Since, we wants <code>VariableDeclaration</code> will pass this as a type.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
    <span class="hljs-keyword">const</span> j = api.jscodeshift;
    <span class="hljs-keyword">const</span> root = j(file.source);

    <span class="hljs-keyword">const</span> variableDeclarations = root.find(j.VariableDeclaration);
}
</code></pre>
<p>Now the <code>variableDeclarations</code> is a an array as it will have all the variableDeclarations node, so we need to loop through it and reach to the correct node or property</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747583347557/4b70740f-e97d-47c1-8b1e-7a2209753bfa.png" alt class="image--center mx-auto" /></p>
<p>Now looping thorugh <code>variableDeclarations</code> will give a <strong>Collection</strong> of nodepaths which has a <code>.value</code> property.Also <code>Declarations</code> is also an array So we need to loop through again to go to the <code>history</code> and that is inside <code>declarations[i] &gt; id &gt; name</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
    <span class="hljs-keyword">const</span> j = api.jscodeshift;
    <span class="hljs-keyword">const</span> root = j(file.source);

    <span class="hljs-keyword">const</span> variableDeclarations = root.find(j.VariableDeclaration);

    variableDeclarations.forEach(<span class="hljs-function">(<span class="hljs-params">declaration</span>) =&gt;</span> {
        declaration.value.declarations.forEach(<span class="hljs-function">(<span class="hljs-params">declarator</span>) =&gt;</span> {
              <span class="hljs-keyword">if</span> (declarator.id.name === <span class="hljs-string">'history'</span>) {
                    declarator.id.name = <span class="hljs-string">'navigate'</span>;
              }
        });
  });
}
</code></pre>
<p>similarly for the <code>useHistory</code> it is inside the <code>declaration &gt; init &gt; callee &gt; name</code></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1747584855688/d08bf340-03cb-4543-a8d2-e69eaa85bf0c.png" alt class="image--center mx-auto" /></p>
<p>so now to replace that we need to update the <code>name</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
    <span class="hljs-keyword">const</span> j = api.jscodeshift;
    <span class="hljs-keyword">const</span> root = j(file.source);

    <span class="hljs-keyword">const</span> variableDeclarations = root.find(j.VariableDeclaration);

    variableDeclarations.forEach(<span class="hljs-function">(<span class="hljs-params">declaration</span>) =&gt;</span> {
        declaration.value.declarations.forEach(<span class="hljs-function">(<span class="hljs-params">declarator</span>) =&gt;</span> {
              <span class="hljs-keyword">if</span> (declarator.id.name === <span class="hljs-string">'history'</span>) {
                    declarator.id.name = <span class="hljs-string">'navigate'</span>;
                    declarator.init.callee.name = <span class="hljs-string">'useNavigate'</span>;
              }
        });
  });
}
</code></pre>
<p>It’s almost done. we have completed the process of renaming the <code>history</code> → <code>navigate</code> and <code>useHistory</code> → <code>useNavigate</code>. The only then left for us is to return the output from <code>AST</code> back to original source code(<code>js</code>)</p>
<p>and to do that we need to <code>return root.toSource()</code>, where root is the transformed <code>AST</code> and <code>toSource()</code> is converting the <code>AST</code> back to <code>js</code></p>
<pre><code class="lang-javascript"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">transformer</span>(<span class="hljs-params">file, api</span>) </span>{
    <span class="hljs-keyword">const</span> j = api.jscodeshift;
    <span class="hljs-keyword">const</span> root = j(file.source);

    <span class="hljs-keyword">const</span> variableDeclarations = root.find(j.VariableDeclaration);

    variableDeclarations.forEach(<span class="hljs-function">(<span class="hljs-params">declaration</span>) =&gt;</span> {
        declaration.value.declarations.forEach(<span class="hljs-function">(<span class="hljs-params">declarator</span>) =&gt;</span> {
              <span class="hljs-keyword">if</span> (declarator.id.name === <span class="hljs-string">'history'</span>) {
                    declarator.id.name = <span class="hljs-string">'navigate'</span>;
                    declarator.init.callee.name = <span class="hljs-string">'useNavigate'</span>;
              }
        });
  });

  <span class="hljs-keyword">return</span> root.toSource()
}
</code></pre>
<p>Now let’s create a script in <code>package.json</code> to run this codemod, the command will look like this</p>
<p><code>jscodeshift -t &lt;jsocdeshit-script-path-we-created&gt; &lt;source-of-file-we-want-to-update&gt;</code></p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"migrate"</span>: <span class="hljs-string">"jscodeshift -t migrate.js index.js"</span>
  }
</code></pre>
<p>or if we want to pass the source file we can make like this,</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"migrate"</span>: <span class="hljs-string">"jscodeshift -t migrate.js --"</span>
  }
</code></pre>
<pre><code class="lang-bash">npm run migrate src/pages/home
</code></pre>
<p>let’s see the magic</p>
<p><img src="https://s5.ezgif.com/tmp/ezgif-596a4709a15368.gif" alt="Screen Recording 2025-05-18 at 10.09.57 PM.mov [video-to-gif output image]" /></p>
<p>Now we can even make the import statement to change from the <code>useHistory</code> → <code>useNavigate</code>.</p>
<p>Hint: use <code>find(j.ImportDeclaration)</code>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Codemods and AST provide a powerful tool to automate code refactoring at scale. Something to keep in mind is that have some kind of versioning system like <code>git</code> to help some mishaps. Also starts with small repo before running on <code>/src</code>.</p>
<p>Hope you have learned something and will build something cool for your usecase.</p>
<p>Drop a comment, or ping me on twitter/linkedIn. I’d love to see what you are building.</p>
]]></content:encoded></item><item><title><![CDATA[Decoding AI Jargons]]></title><description><![CDATA[Ever feel like tech talk is a foreign language? You're not alone! With every shiny new technology comes a dictionary of jargon that can make your head spin. Fear not, this blog is here to translate tech-speak into plain English, with a dash of humor ...]]></description><link>https://blogs.saurabhsubham.in/decoding-ai-jargons</link><guid isPermaLink="true">https://blogs.saurabhsubham.in/decoding-ai-jargons</guid><category><![CDATA[ChaiCode]]></category><category><![CDATA[genai]]></category><category><![CDATA[GenAI Cohort]]></category><category><![CDATA[chatgpt]]></category><category><![CDATA[Chaiaurcode]]></category><dc:creator><![CDATA[Subham Saurabh]]></dc:creator><pubDate>Wed, 09 Apr 2025 08:47:12 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/eGGFZ5X2LnA/upload/3a4183d36d7963340b31240117cc988a.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Ever feel like tech talk is a foreign language? You're not alone! With every shiny new technology comes a dictionary of jargon that can make your head spin. Fear not, this blog is here to translate tech-speak into plain English, with a dash of humor to keep things fun. Let's dive into the world of tech without the headache!</p>
<h2 id="heading-transformers">Transformers</h2>
<p><img src="https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExZ2pjOGZ0d3BocTZnbGY0azBxMndmbWl0N2sycGI0dHlsenR6bnhwNSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/j9pKCnYLbN1nqYcuHc/giphy.gif" alt class="image--center mx-auto" /></p>
<p>In the realm of generative AI, "transformers" are a type of model architecture that has revolutionized natural language processing. Much like the Autobots and Decepticons from the Transformers universe, these models are powerful and versatile. They can transform input data into meaningful output, whether it's translating languages, generating text, or even creating images.</p>
<p>Transformers work by using a mechanism called "attention," which allows them to focus on different parts of the input data when producing an output. This is akin to how Autobots and Decepticons can change their forms to adapt to different situations. The attention mechanism helps the model understand context and relationships within the data, making it highly effective for tasks that require understanding complex patterns and sequences.</p>
<h2 id="heading-gpt">GPT</h2>
<p>GPT stands for generative pre-trained transformer. Here's a simpler breakdown:</p>
<ul>
<li><p>"Generative" means it can create new text (or other forms) based on the input it receives.</p>
</li>
<li><p>"Pre-trained" means it has already learned a lot about language from a big collection of text before being used for specific tasks.Since it is pre-trained it will have a cut-off date</p>
</li>
<li><p>"Transformer" is the type of model it uses, which helps it understand and generate text efficiently.</p>
</li>
</ul>
<p>Together, these parts make GPT good at understanding and creating human-like text.</p>
<h2 id="heading-encoder-and-decoder">Encoder and Decoder</h2>
<p>Transformers don't naturally understand English; they interpret data as numbers. To do this, we need to convert our text into numbers, a process called encoding. Encoding changes the input text into a numerical form that the transformer can work with.</p>
<p>After encoding, we also need a way to change it back to its original form. This reverse process, called decoding, lets the transformer reconstruct the initial input from the numbers, ensuring the information is accurately understood and used.</p>
<p>Think of it like a spy sending a message. They can't send it in plain English because they might get caught, so they encode the message. Even if someone else reads it, they won't understand it. At the receiving end, the message is converted back to normal English through decoding. Similarly, GPT understands numbers, so we encode our input into numbers, and GPT converts it back to the original format using decoding.</p>
<p>You can visualise this on <a target="_blank" href="https://tiktokenizer.vercel.app/">tiktokenizer</a> link.</p>
<p>I have created my own encoding and decoding program and you can access it on this <a target="_blank" href="https://saurabhsubham113.github.io/gen-ai-encoder/">link</a>.</p>
<p>Here is a quick demo</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744188112069/3c10e6f3-3cae-41dc-9f6b-62f2ee2c2805.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-vector-embedding">Vector Embedding</h2>
<p>Imagine every word in a sentence is like an item in a big super mart. Now, you want to group similar items together — all the fruits in one rack, all spices in another, and snacks in one corner. That’s exactly what <strong>vector embeddings</strong> do. They turn each word into a number-based location — kind of like giving it a spot on Google Maps. Words that are similar (like <em>king</em> and <em>queen</em>, or <em>pani puri</em> and <em>chaat</em>) are placed closer on this map. So when an AI model “looks” at words, it doesn’t just see random text — it sees them like neatly arranged grocery aisles. This helps it figure out meaning, tone, and relationships — just like you know that aloo and bhindi go in the same sabzi section.</p>
<p>Thanks to vector embeddings, AI can:</p>
<ul>
<li><p>Understand what you’re talking about</p>
</li>
<li><p>Reply more naturally</p>
</li>
<li><p>Group similar things together (like in recommendation systems)</p>
</li>
</ul>
<p>Want to <em>see</em> how this works visually? <a target="_blank" href="https://projector.tensorflow.org/">Check this out</a> — it’s super cool!</p>
<h2 id="heading-postional-encoding">Postional Encoding</h2>
<p>Imagine you’re at a wedding buffet. You see <em>roti</em>, then <em>dal</em>, then <em>paneer</em>, then <em>gulab jamun</em>. Now imagine if someone randomly shuffled it to <em>gulab jamun</em>, <em>dal</em>, <em>paneer</em>, <em>roti</em>. Ufff. Total confusion, right?</p>
<p>That’s where <strong>positional encoding</strong> comes in for AI.</p>
<p>LLMs (like ChatGPT) don’t <em>naturally</em> understand word order. They just see a bunch of words. So we have to <strong>tell</strong> the model where each word is in the sentence — like giving it a seat number at a shaadi.</p>
<p>For example:</p>
<ul>
<li><p>“<strong>Ram ate mango</strong>” → makes sense.</p>
</li>
<li><p>“<strong>Mango ate Ram</strong>” → now the mango is a cannibal?</p>
</li>
</ul>
<p>So we assign <em>positional values</em> to each word to maintain meaning. Just like you know starters come before main course and dessert comes last, the AI understands sentence flow better when we tag word positions.It’s like adding a “row number” to your Excel sheet — the data’s the same, but now it’s organized. Without positional encoding, an AI model would treat “I love biryani” and “Biryani loves I” as the same thing.</p>
<h2 id="heading-semantic-meaning">Semantic Meaning</h2>
<p>Let’s say you’re watching an India vs Pakistan match. Kohli walks out to bat, the stadium is roaring, expectations are sky-high. But then — he gets out on the very first ball. The next day, someone says, “Kohli broke millions of hearts yesterday.” Now if an AI model takes that sentence literally, it might think Kohli was involved in some emotional drama. But as an Indian cricket fan, you understand the real meaning — he got out early, and fans were hugely disappointed. That’s what <strong>semantic meaning</strong> is all about: understanding the <strong>real intent</strong> behind words based on context — not just what the words say, but what they actually <strong>mean</strong> in that situation.</p>
<p>This is a big deal in AI. To truly “understand” language, models need to figure out these hidden meanings. For example, the sentence “the crowd went silent” could mean people are bored, shocked, or deeply emotional — and the model needs to infer the <strong>right</strong> reason based on the overall context.</p>
<p>AI models learn this by studying how words appear together in huge amounts of text. They start seeing patterns — like “Kohli,” “match,” “out,” and “fans” often appearing together in emotional contexts. This helps the model connect the dots and guess the intended meaning.</p>
<h2 id="heading-self-attention">Self Attention</h2>
<p>Imagine you’re reading an Indian newspaper headline:</p>
<blockquote>
<p>“Modi met Shah at Delhi airport after the G20 summit.”</p>
</blockquote>
<p>Now, as a human, you naturally understand <strong>who did what, where, and when</strong>. You know that “Modi” and “Shah” are people, “met” is the action, “Delhi airport” is the place, and “G20 summit” gives you context. But for an AI model, it needs to <strong>pay attention</strong> to the right words at the right time. This is where <strong>Self-Attention</strong> comes in — it helps the model <strong>focus</strong> on <strong>important words</strong> in a sentence, depending on what it’s trying to understand or generate.</p>
<p>In AI, when a model reads a word like “met,” it looks at <strong>all the other words in the sentence</strong> and decides which ones are <strong>relevant</strong>. For “met,” the model might pay more attention to “Modi” and “Shah” to figure out <strong>who met whom</strong>.</p>
<p>Self-attention assigns <strong>weights</strong> to each word — higher weights to more relevant ones. That’s how the model keeps track of relationships in a sentence, no matter how long it is.</p>
<h2 id="heading-multi-head-attention">Multi Head Attention</h2>
<p><strong>Multi-Head Attention</strong> is like watching an IPL match with a group of friends—everyone’s watching the same game, but each person is noticing something different. One is focused on Kohli’s stance, another is watching the bowler’s grip, someone’s tracking the field setup, and someone else is reacting to the crowd. That’s exactly what multi-head attention does: it lets a transformer model look at the same sentence from different angles, all at once.</p>
<p>Each “head” in this mechanism acts like a specialist. One might focus on the relationship between subjects and verbs, another on sentiment, and yet another on word positions. These heads process the sentence in parallel, and their individual insights are later combined, giving the model a richer, more detailed understanding of the input. It’s like assembling different pieces of gossip from your cousins to figure out what really happened at the wedding.</p>
<p>For instance, in the sentence <em>“Virat hit the ball and the crowd went wild,”</em> one head might link “Virat” to “hit,” another might tie “ball” to “crowd,” and a third might interpret the emotional shift. This multi-perspective view makes language models incredibly good at picking up nuances—whether it’s translating Tamil to English or understanding sarcastic memes on Indian Twitter.</p>
<h2 id="heading-softmax">Softmax</h2>
<p>Imagine you and your friends are choosing where to eat. You list your top 3 picks: pizza, biryani, and dosa. Everyone gives a score out of 10 for each option. Instead of just picking the one with the highest score, you want to <strong>see how likely each place is to be chosen</strong> based on everyone's preferences. This is where <strong>Softmax</strong> helps.</p>
<p>In machine learning, Softmax takes a bunch of raw scores (called logits) — which might be any number, positive or negative — and <strong>converts them into probabilities</strong> that are easy to compare. It does this by making the biggest numbers stand out even more and squashing the rest proportionally. So if biryani gets a score of 10, pizza gets 7, and dosa gets 2, Softmax will say something like: “Biryani has an 80% chance, pizza 18%, and dosa 2%.”</p>
<p>It’s like your brain thinking, “I’m mostly in the mood for biryani, but I could settle for pizza. Dosa? Not today.” This helps AI models make decisions confidently by understanding <strong>how strong</strong> each option is relative to the rest. Softmax doesn’t just look at the best option — it compares all of them to make a <strong>well-balanced decision</strong>.</p>
<h2 id="heading-temperature">Temperature</h2>
<p>Imagine you’re using an AI chatbot to generate pickup lines (no judgment). Now, if you set the <strong>temperature</strong> to <strong>0</strong>, the model plays it super safe — it’ll always choose the most predictable, bland line like, <em>“Are you a library book? Because I want to check you out.”</em> Meh. Reliable? Yes. Spicy? Not at all.</p>
<p>Now crank the temperature up to <strong>1.0 or higher</strong>, and suddenly the AI gets a little wild. It might say something like, <em>“Are you made of copper and tellurium? Because you’re Cu-Te, but also highly reactive in my neural circuits.”</em> Riskier, funnier, and more creative — but it could also backfire and say something totally off-beat. That’s the trade-off.</p>
<p>In short: <strong>Temperature controls randomness.</strong> A <strong>low temperature</strong> makes the model stick to safe, high-confidence responses. A <strong>high temperature</strong> lets it explore less likely, but more diverse or creative outputs. Think of it like masala levels in your food — too low and it’s bland, too high and it might just burn your brain. The sweet spot depends on the vibe you’re going for.</p>
<h2 id="heading-softmax-vs-temperature">Softmax vs Temperature</h2>
<p>Imagine you’re at a <strong>chaat stall</strong> in Delhi. The vendor asks, “What would you like—Pani Puri, Dahi Puri, or Aloo Tikki?” Your cravings are like <em>logits</em> (raw scores)—you’re leaning more toward Pani Puri, but the other options are still tempting. Now, <strong>Softmax</strong> is like your brain calculating the odds: “Hmm, I love Pani Puri (60%), but I’m also in the mood for Aloo Tikki (30%), and maybe just a little Dahi Puri (10%).” Softmax takes your raw cravings and turns them into neat, sharable probabilities.</p>
<p>But what if your mood isn’t stable? That’s where <strong>Temperature</strong> comes in. Let’s say your hunger level is <em>low</em> (temperature = 0.3). You’ll go straight for Pani Puri, no second thoughts—zero randomness. But if you’re in an adventurous mood (temperature = 1.5), you might say, “Why not try Dahi Puri today, even if it wasn’t the top pick?” With higher temperature, you’re more open to exploring new tastes. Lower temperature? You’re sticking to tried-and-tested.</p>
<p>So in AI, Softmax is the logic that turns model guesses into probabilities. <strong>Temperature is the spice meter</strong>—it decides whether the model sticks to its top guess or experiments with something offbeat. Together, they help models balance between <strong>sensible and surprising</strong>, just like we do at a food stall!</p>
<h2 id="heading-knowledge-cutoff">Knowledge cutoff</h2>
<p>A <strong>knowledge cutoff</strong> is like a student who stopped studying the news after a certain date—let’s say they last read the newspaper in December 2023. So, if you ask them about the 2024 Lok Sabha elections or who won IPL 2025, they’ll blank out. Similarly, AI models like me are trained on a snapshot of the internet up to a certain point (for me, it’s <strong>June 2024</strong>). Anything that happened after that—new tech releases, policy changes, cricket wins, viral memes—I won’t know unless I’m updated. So, when talking to an AI, always remember: it’s not ignoring you, it’s just stuck in the past like that friend who still thinks TikTok is banned in India.</p>
<h2 id="heading-conclusion-ai-doesnt-have-to-be-rocket-science"><strong>Conclusion: AI Doesn’t Have to Be Rocket Science</strong></h2>
<p>If you’ve made it this far—congrats! You’ve just unpacked some of the trickiest AI jargons out there, and hopefully, they now feel more like familiar roadside dhabas than alien tech towers.</p>
<p>From <strong>tokenization</strong> slicing up text like your mom chops veggies for sabzi, to <strong>vector embeddings</strong> helping AI understand context like how we know “Kohli” means cricket and not chemistry, you’ve started peeling back the layers of how machines process language. <strong>Positional encoding</strong> showed us that order matters—just like your roll number did in school. <strong>Softmax</strong> and <strong>temperature</strong> taught us how models decide <em>what</em> to say and <em>how creatively</em> to say it, while <strong>self-attention</strong> and <strong>multi-head attention</strong> proved that models aren’t just looking at words—they’re analyzing the full picture, much like how you scan your friends’ faces before cracking a joke.</p>
<p>And finally, <strong>knowledge cutoff</strong>? That’s just the AI saying, “Bro, I stopped reading the news after June 2024.”</p>
<p>This is just the beginning. The more we break down these buzzwords, the more power we gain to understand—and maybe even shape—the AI shaping our world. Stay curious, ask dumb questions (they’re usually the best ones), and remember: you don’t need a PhD to decode AI. Sometimes, all it takes is a little masala and a lot of curiosity. 🚀</p>
]]></content:encoded></item><item><title><![CDATA[Easy Setup and Execution of Cypress Tests in GitLab CI]]></title><description><![CDATA[In today’s fast-paced development environment, continuous integration and testing have become crucial for delivering high-quality software. Cypress, a powerful end-to-end testing framework, paired with GitLab CI, a robust continuous integration tool,...]]></description><link>https://blogs.saurabhsubham.in/easy-setup-and-execution-of-cypress-tests-in-gitlab-ci</link><guid isPermaLink="true">https://blogs.saurabhsubham.in/easy-setup-and-execution-of-cypress-tests-in-gitlab-ci</guid><category><![CDATA[Cypress]]></category><category><![CDATA[cypress testing]]></category><category><![CDATA[cypress automation]]></category><category><![CDATA[Testing]]></category><dc:creator><![CDATA[Subham Saurabh]]></dc:creator><pubDate>Sun, 18 Aug 2024 18:43:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/ZV_64LdGoao/upload/857fec509abb7229cfbc39280d48d0c0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In today’s fast-paced development environment, continuous integration and testing have become crucial for delivering high-quality software. Cypress, a powerful end-to-end testing framework, paired with GitLab CI, a robust continuous integration tool, can streamline your testing process and help catch bugs early. In this guide, we’ll walk you through the steps to set up Cypress tests in GitLab CI.</p>
<h3 id="heading-why-cypress"><strong>Why Cypress?</strong></h3>
<p>Cypress is gaining popularity due to its developer-friendly nature, real-time reloads, and powerful testing capabilities. It’s an all-in-one testing framework that supports end-to-end testing, unit testing, and integration testing. Cypress provides detailed and interactive error reports, making it easier to debug tests.</p>
<p><strong>Prerequisites</strong></p>
<p>Before diving in, ensure you have the following:</p>
<p>• A GitLab repository with your project code.</p>
<p>• A basic understanding of GitLab CI/CD pipelines.</p>
<h3 id="heading-step-1-set-up-cypress-in-your-project"><strong>Step 1: Set Up Cypress in Your Project</strong></h3>
<p>If you haven’t already installed Cypress in your project, you can do so with npm or yarn:</p>
<pre><code class="lang-bash">npm install cypress --save-dev
</code></pre>
<p>or</p>
<pre><code class="lang-bash">yarn cypress --dev
</code></pre>
<p>Once installed, you can initialize Cypress by running</p>
<pre><code class="lang-bash">npx cypress open
</code></pre>
<p>This command sets up the necessary folder structure and configuration files. You’ll find a <code>cypress.config.js</code> file in the root directory, where you can configure Cypress settings.</p>
<h3 id="heading-step-2-add-code-coverage"><strong>Step 2: Add Code coverage</strong></h3>
<p>You need to install <code>@cypress/code-coverage</code> and <code>nyc</code> (Istanbul's command-line interface) in your project:</p>
<pre><code class="lang-bash">npm install --save-dev @cypress/code-coverage nyc
</code></pre>
<p>In your <code>cypress.config.js</code> file, add the following configuration to include the code coverage plugin:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { defineConfig } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cypress'</span>);

<span class="hljs-built_in">module</span>.exports = defineConfig({
  <span class="hljs-attr">e2e</span>: {
    setupNodeEvents(on, config) {
      <span class="hljs-built_in">require</span>(<span class="hljs-string">'@cypress/code-coverage/task'</span>)(on, config);
      <span class="hljs-keyword">return</span> config;
    },
  },
});
</code></pre>
<p>If you want to add it for component testing you can add the</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { defineConfig } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cypress'</span>);

<span class="hljs-built_in">module</span>.exports = defineConfig({
  <span class="hljs-attr">e2e</span>: {
    setupNodeEvents(on, config) {
      <span class="hljs-built_in">require</span>(<span class="hljs-string">'@cypress/code-coverage/task'</span>)(on, config);
      <span class="hljs-keyword">return</span> config;
    },
  },
  <span class="hljs-attr">component</span>: {
     setupNodeEvents(on, config) {
       <span class="hljs-built_in">require</span>(<span class="hljs-string">'@cypress/code-coverage/task'</span>)(on, config);
       <span class="hljs-keyword">return</span> config;
    },
  },
});
</code></pre>
<p>Create or update the <code>cypress/support/command.js</code> file to include the code coverage support:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> <span class="hljs-string">'@cypress/code-coverage/support'</span>;
</code></pre>
<p>also, import the <code>cypress/support/command.js</code> file into <code>cypress/support/e2e.js</code> and <code>cypress/support/component.js</code> to get the coverage for the e2e and component test files.</p>
<p>To generate the code coverage report, you can use the following command:</p>
<pre><code class="lang-bash">nyc report --reporter=&lt;reporter&gt;
</code></pre>
<p>You can replace <code>&lt;reporter&gt;</code> with any of the available reporter options provided by <code>nyc</code>. Some common reporter options include:</p>
<ul>
<li><p><code>html</code>: Generates an HTML report.</p>
</li>
<li><p><code>text</code>: Outputs the report in plain text.</p>
</li>
<li><p><code>text-summary</code>: Provides a summary in plain text.</p>
</li>
<li><p><code>lcov</code>: Generates an lcov report, which can be used with various CI tools.</p>
</li>
<li><p><code>json</code>: Outputs the report in JSON format.</p>
</li>
</ul>
<p>For example, to generate an HTML report, you would use:</p>
<pre><code class="lang-bash">npx nyc report --reporter=html
</code></pre>
<p>You can specify multiple reporters by separating them with a space:</p>
<pre><code class="lang-bash">npx nyc report --reporter=html --reporter=text-summary\
</code></pre>
<p>This command will generate both an HTML report and a text summary of the code coverage.</p>
<h3 id="heading-step-3-create-your-cypress-tests"><strong>Step 3: Create Your Cypress Tests</strong></h3>
<p>Cypress organizes tests into specs, which are just JavaScript files. You can create a new spec file in the <code>cypress/e2e/mytest.cy.js</code> folder:</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'My First Test'</span>, <span class="hljs-function">() =&gt;</span> {
  it(<span class="hljs-string">'Visits the Kitchen Sink'</span>, <span class="hljs-function">() =&gt;</span> {
    cy.visit(<span class="hljs-string">'https://example.cypress.io'</span>)
    cy.contains(<span class="hljs-string">'type'</span>).click()
    cy.url().should(<span class="hljs-string">'include'</span>, <span class="hljs-string">'/commands/actions'</span>)
    cy.get(<span class="hljs-string">'.action-email'</span>)
      .type(<span class="hljs-string">'fake@email.com'</span>)
      .should(<span class="hljs-string">'have.value'</span>, <span class="hljs-string">'fake@email.com'</span>)
  })
})
</code></pre>
<p>This is a simple test that visits the Cypress example page, clicks a link, and asserts that the URL contains /commands/actions. Cypress provides a clean syntax to interact with web elements and make assertions.</p>
<h3 id="heading-step-4-configure-gitlab-ci"><strong>Step 4: Configure GitLab CI</strong></h3>
<p>Next, we need to set up GitLab CI to run our Cypress tests automatically. Start by creating a .gitlab-ci.yml file in the root directory of your project if you don’t already have one.</p>
<p>Here’s a basic configuration to run Cypress in GitLab CI:</p>
<pre><code class="lang-yaml">
<span class="hljs-attr">stages:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">test</span>

<span class="hljs-attr">test:</span>
  <span class="hljs-attr">image:</span> <span class="hljs-string">cypress/base:10</span> <span class="hljs-comment"># you can use different docker image on cypress official website</span>
  <span class="hljs-attr">stage:</span> <span class="hljs-string">test</span>
  <span class="hljs-attr">variables:</span>
    <span class="hljs-attr">NODE_ENV:</span> <span class="hljs-string">test</span> <span class="hljs-comment"># if you want to set up the node_env to test</span>
    <span class="hljs-attr">STATEMENT:</span> <span class="hljs-number">50</span>  <span class="hljs-comment"># Set your desired threshold for statements to be covered</span>
    <span class="hljs-attr">FUNCTION:</span> <span class="hljs-number">50</span>   <span class="hljs-comment"># Set your desired threshold for functions to be covered</span>
    <span class="hljs-attr">BRANCHES:</span> <span class="hljs-number">50</span>   <span class="hljs-comment"># Set your desired threshold for branches to be covered</span>
    <span class="hljs-attr">LINES:</span> <span class="hljs-number">50</span>      <span class="hljs-comment"># Set your desired threshold for lines to be covered</span>
  <span class="hljs-attr">cache:</span>
    <span class="hljs-attr">key:</span> <span class="hljs-string">${CI_COMMIT_REF_SLUG}-setup</span>
    <span class="hljs-attr">paths:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">node_modules/</span>
  <span class="hljs-attr">before_script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>
  <span class="hljs-attr">script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npx</span> <span class="hljs-string">cypress</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># Explicitly install Cypress as a fallback</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npx</span> <span class="hljs-string">cypress</span> <span class="hljs-string">run</span> <span class="hljs-string">--headless</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npx</span> <span class="hljs-string">nyc</span> <span class="hljs-string">check-coverage</span> <span class="hljs-string">--lines</span> <span class="hljs-string">$LINES</span> <span class="hljs-string">--statements</span> <span class="hljs-string">$STATEMENT</span> <span class="hljs-string">--functions</span> <span class="hljs-string">$FUNCTION</span> <span class="hljs-string">--branches</span> <span class="hljs-string">$BRANCHES</span>
  <span class="hljs-attr">after_script:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-string">npx</span> <span class="hljs-string">nyc</span> <span class="hljs-string">report</span> <span class="hljs-string">--reporter=cobertura</span> <span class="hljs-string">--reporter=text-summary</span>
  <span class="hljs-attr">coverage:</span> <span class="hljs-string">'/^Statements\s*:\s*([^%]+%)/'</span>
  <span class="hljs-attr">allow_failure:</span> <span class="hljs-literal">true</span> <span class="hljs-comment"># this will fail the pipeline</span>
  <span class="hljs-attr">artifacts:</span>
    <span class="hljs-attr">paths:</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">coverage/lcov-report/index.html</span>
     <span class="hljs-bullet">-</span> <span class="hljs-string">coverage/cobertura-coverage.xml</span>
    <span class="hljs-attr">reports:</span>
      <span class="hljs-attr">coverage_report:</span>
        <span class="hljs-attr">coverage_format:</span> <span class="hljs-string">cobertura</span>
        <span class="hljs-attr">path:</span> <span class="hljs-string">coverage/cobertura-coverage.xml</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">if:</span> <span class="hljs-string">$CI_PIPELINE_SOURCE</span> <span class="hljs-string">==</span> <span class="hljs-string">'merge_request_event'</span>
    <span class="hljs-bullet">-</span> <span class="hljs-attr">if:</span> <span class="hljs-string">$CI_COMMIT_BRANCH</span> <span class="hljs-string">==</span> <span class="hljs-string">'master'</span>
</code></pre>
<p>This configuration is designed to run Cypress tests in a GitLab CI pipeline with NYC for coverage checking. It ensures that your code meets the defined coverage thresholds before being merged or deployed.</p>
<p>• <strong>Stages:</strong> Defines the stages in the CI/CD pipeline. In this case, there is a single test stage where the Cypress tests will be executed.</p>
<p>• <strong>Test Job:</strong> This is the job that runs in the test stage.</p>
<p>• <strong>Image:</strong> Uses cypress/base:10 Docker image, which has Node.js and Cypress pre-installed. You can use different versions or images based on your requirements.</p>
<p>• <strong>Variables:</strong></p>
<p>• NODE_ENV: test: Sets the environment to test mode.</p>
<p>• STATEMENT, FUNCTION, BRANCHES, LINES: These variables define the minimum coverage thresholds (in percentage) for statements, functions, branches, and lines. If your tests do not meet these thresholds, the job will fail.</p>
<p>• <strong>Cache:</strong> Caches the node_modules/ directory to speed up subsequent pipeline runs. The cache key is based on the current branch, ensuring different branches do not conflict.</p>
<p>• <strong>Before Script:</strong> This command installs the project dependencies using npm ci, which is faster and more reliable in CI environments compared to npm install.</p>
<p>• <strong>Script:</strong></p>
<p>• npx cypress install: Explicitly installs Cypress, ensuring it’s available even if something goes wrong with the initial setup.</p>
<p>• npx cypress run --headless: Runs Cypress tests in headless mode, meaning no browser UI is shown, which is typical in CI environments.</p>
<p>• npx nyc check-coverage: Checks the test coverage against the defined thresholds (LINES, STATEMENT, FUNCTION, BRANCHES). If the coverage falls below these thresholds, the job will fail.</p>
<p>• <strong>After Script:</strong> Generates coverage reports in cobertura format (which is compatible with many CI tools) and a text summary, allowing you to easily review coverage results.</p>
<p>• <strong>Coverage Regex:</strong> A regular expression that GitLab uses to parse the coverage percentage from the job output, enabling the coverage percentage to be displayed in the merge request widget.</p>
<p>• <strong>Allow Failure:</strong> This option allows the job to fail without failing the entire pipeline. This is useful for non-blocking jobs where you might want to allow code to be merged even if coverage is below the threshold.</p>
<p>• <strong>Artifacts:</strong> Specifies the files to be retained after the job completes. Here, the coverage report in cobertura format is stored as an artifact, which can be accessed later for review.</p>
<p>• <strong>Rules:</strong> Defines conditions under which the job will run. In this case, the job will run for merge requests and when committing directly to the master branch.</p>
<h3 id="heading-step-5-push-to-gitlab-and-trigger-the-pipeline"><strong>Step 5: Push to GitLab and Trigger the Pipeline</strong></h3>
<p>Once your .gitlab-ci.yml file is ready, commit and push the changes to your GitLab repository:</p>
<pre><code class="lang-bash">git add .gitlab-ci.yml
git commit -m <span class="hljs-string">"Add GitLab CI configuration for Cypress tests"</span>
git push origin main
</code></pre>
<p>This will trigger a new pipeline in GitLab. You can monitor the progress of your pipeline by navigating to the <strong>CI/CD &gt; Pipelines</strong> section in your GitLab project or you can create a merge request to master.</p>
<h3 id="heading-step-6-reviewing-test-results"><strong>Step 6: Reviewing Test Results</strong></h3>
<p>When the pipeline completes, you can review the test results directly in the GitLab interface. If any tests fail, you’ll be able to download the screenshots and videos captured during the test run, which can be invaluable for debugging.</p>
<p>Additionally, the test results can be viewed on the Merge Request (MR) dashboard, providing a convenient overview of the test outcomes and making it easier to identify and address issues.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724005559495/0aa7d439-40a9-4671-8de2-ff098a56b8f8.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-6-advanced-configuration-optional"><strong>Step 6: Advanced Configuration (Optional)</strong></h3>
<p>For larger projects, you might want to parallelize tests to reduce the overall runtime. Cypress supports test parallelization natively, but you’ll need to configure your GitLab runner accordingly and use Cypress Dashboard to manage parallel runs.</p>
<p>Another advanced configuration involves setting environment variables for different stages, such as running tests in different environments (staging, production) or using custom configurations for different types of tests.</p>
<h3 id="heading-conclusion"><strong>Conclusion</strong></h3>
<p>By integrating Cypress with GitLab CI, you’re taking a big step towards ensuring your codebase remains bug-free and stable. Automated tests run on every push, catching issues before they reach production. This setup not only saves time but also boosts confidence in your code.</p>
<p>With the basics covered, you can now expand on this foundation by exploring advanced Cypress features, optimizing your pipeline, or integrating other tools like Slack for notifications. Happy testing!</p>
<p>This guide should help developers set up Cypress testing in GitLab CI. If you need any more details or specific sections expanded, just let me know!</p>
]]></content:encoded></item></channel></rss>