<?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" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ YAML - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ YAML - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sat, 30 May 2026 16:31:23 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/yaml/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Work with YAML in Python – A Guide with Examples ]]>
                </title>
                <description>
                    <![CDATA[ If you've ever worked with configuration files, Docker Compose, Kubernetes, or CI/CD pipelines, you've probably used YAML. It's everywhere in modern development, and for good reason: it’s human-readable, simple, and powerful. In this guide, you'll le... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-work-with-yaml-in-python-a-guide-with-examples/</link>
                <guid isPermaLink="false">6939fb2773d2f31ad28cfef5</guid>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bala Priya C ]]>
                </dc:creator>
                <pubDate>Wed, 10 Dec 2025 22:58:47 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1765407508788/61769835-bd12-486e-8f8e-ba0f3a7af83c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you've ever worked with configuration files, Docker Compose, Kubernetes, or CI/CD pipelines, you've probably used YAML. It's everywhere in modern development, and for good reason: it’s human-readable, simple, and powerful.</p>
<p>In this guide, you'll learn how to work with YAML files in Python. We'll cover reading, writing, and manipulating YAML data in practice.</p>
<p>🔗 <a target="_blank" href="https://github.com/balapriyac/python-basics/tree/main/config-management-basics/working-with-yaml"><strong>You can find the code on GitHub</strong></a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before working with YAML in Python, you should have:</p>
<ul>
<li><p>Python 3.8 or a later version installed</p>
</li>
<li><p><strong>Basic Python knowledge</strong>: Variables, data types, functions, and control structures</p>
</li>
<li><p><strong>Understanding of data structures</strong>: Dictionaries, lists, and nested data structures</p>
</li>
<li><p><strong>File handling basics</strong>: Reading from and writing to files in Python</p>
</li>
<li><p><strong>Command line familiarity</strong>: Running Python scripts and installing packages with <code>pip</code></p>
</li>
</ul>
<p>You'll also need to install the <a target="_blank" href="https://pypi.org/project/PyYAML/">PyYAML</a> library:</p>
<pre><code class="lang-bash">pip install pyyaml
</code></pre>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-yaml-and-why-should-you-care">What Is YAML and Why Should You Care?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-yaml-files">How to Read YAML Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-yaml-files">How to Write YAML Files</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-work-with-lists-in-yaml">How to Work with Lists in YAML</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-build-a-yaml-config-manager">Build a YAML Config Manager</a></p>
</li>
</ol>
<h2 id="heading-what-is-yaml-and-why-should-you-care">What Is YAML and Why Should You Care?</h2>
<p>YAML (YAML Ain't Markup Language) is a data serialization format designed to be easy to read and write. Think of it as JSON's more readable cousin. :)</p>
<p>Here's the same data in JSON and YAML:</p>
<p>JSON:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"database"</span>: {
    <span class="hljs-attr">"host"</span>: <span class="hljs-string">"localhost"</span>,
    <span class="hljs-attr">"port"</span>: <span class="hljs-number">5432</span>,
    <span class="hljs-attr">"credentials"</span>: {
      <span class="hljs-attr">"username"</span>: <span class="hljs-string">"admin"</span>,
      <span class="hljs-attr">"password"</span>: <span class="hljs-string">"secret"</span>
    }
  }
}
</code></pre>
<p>YAML:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">database:</span>
  <span class="hljs-attr">host:</span> <span class="hljs-string">localhost</span>
  <span class="hljs-attr">port:</span> <span class="hljs-number">5432</span>
  <span class="hljs-attr">credentials:</span>
    <span class="hljs-attr">username:</span> <span class="hljs-string">admin</span>
    <span class="hljs-attr">password:</span> <span class="hljs-string">secret</span>
</code></pre>
<p>The YAML version is cleaner and easier to read, especially for configuration files.</p>
<h2 id="heading-how-to-read-yaml-files">How to Read YAML Files</h2>
<p>Let's say you have a configuration file for a web application. We'll create a simple <a target="_blank" href="https://github.com/balapriyac/python-basics/blob/main/config-management-basics/working-with-yaml/config.yaml"><code>config.yaml</code></a> file and learn how to read it in Python.</p>
<p>First, let's understand what we're trying to do. You have configuration data stored in a YAML file, and you want to load it into Python so you can use it in your application. Here’s how you can do it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml

<span class="hljs-comment"># Open and read the YAML file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'config.yaml'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
    config = yaml.safe_load(file)

<span class="hljs-comment"># Access the data</span>
print(config[<span class="hljs-string">'database'</span>][<span class="hljs-string">'host'</span>])
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">localhost
</code></pre>
<p>Here's what's happening in this code:</p>
<ul>
<li><p>We import the <code>yaml</code> module.</p>
</li>
<li><p>Then we open the file using a context manager (<code>with</code> statement), which automatically closes the file when we're done.</p>
</li>
<li><p>We use <code>yaml.safe_load()</code> to parse the YAML content into a Python dictionary so we can access the data just like any Python dictionary.</p>
</li>
</ul>
<p>⚠️ Note that you should <strong>always use</strong> <code>yaml.safe_load()</code> <strong>instead of</strong> <code>yaml.load()</code><strong>.</strong> The <code>safe_load()</code> function protects you from arbitrary code execution vulnerabilities. Unless you have a very specific reason (and you probably don't), stick with <code>safe_load()</code>.</p>
<h2 id="heading-how-to-write-yaml-files">How to Write YAML Files</h2>
<p>Now let's go in the opposite direction. You have Python data structures and you want to save them as YAML files. This is useful when you're generating configuration files or exporting data.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml

<span class="hljs-comment"># Your configuration data as Python dictionaries</span>
config = {
    <span class="hljs-string">'database'</span>: {
        <span class="hljs-string">'host'</span>: <span class="hljs-string">'localhost'</span>,
        <span class="hljs-string">'port'</span>: <span class="hljs-number">5432</span>,
        <span class="hljs-string">'name'</span>: <span class="hljs-string">'myapp_db'</span>,
        <span class="hljs-string">'credentials'</span>: {
            <span class="hljs-string">'username'</span>: <span class="hljs-string">'admin'</span>,
            <span class="hljs-string">'password'</span>: <span class="hljs-string">'secret123'</span>
        }
    },
    <span class="hljs-string">'server'</span>: {
        <span class="hljs-string">'host'</span>: <span class="hljs-string">'0.0.0.0'</span>,
        <span class="hljs-string">'port'</span>: <span class="hljs-number">8000</span>,
        <span class="hljs-string">'debug'</span>: <span class="hljs-literal">True</span>
    },
    <span class="hljs-string">'features'</span>: {
        <span class="hljs-string">'enable_cache'</span>: <span class="hljs-literal">True</span>,
        <span class="hljs-string">'cache_ttl'</span>: <span class="hljs-number">3600</span>
    }
}

<span class="hljs-comment"># Write to a YAML file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'generated_config.yaml'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> file:
    yaml.dump(config, file, default_flow_style=<span class="hljs-literal">False</span>)
</code></pre>
<p>Let's break down what's happening:</p>
<ul>
<li><p>We create a nested Python dictionary with our configuration.</p>
</li>
<li><p>We open a file in write mode (<code>'w'</code>).</p>
</li>
<li><p>We use <code>yaml.dump()</code> to convert the Python dictionary to YAML format and write it to the file.</p>
</li>
<li><p>The <code>default_flow_style=False</code> parameter ensures the output uses block style (the readable, indented format) instead of inline style.</p>
</li>
</ul>
<p>The resulting <code>generated_config.yaml</code> file will be properly formatted and ready to use.</p>
<h2 id="heading-how-to-work-with-lists-in-yaml">How to Work with Lists in YAML</h2>
<p>YAML handles lists elegantly, and they're common in configuration files. Suppose you're building a microservices application and need to configure multiple service endpoints. Here's how you'd work with that data:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml

<span class="hljs-comment"># Configuration with lists</span>
services_config = {
    <span class="hljs-string">'services'</span>: [
        {
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'auth-service'</span>,
            <span class="hljs-string">'url'</span>: <span class="hljs-string">'http://auth.example.com'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">30</span>
        },
        {
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'payment-service'</span>,
            <span class="hljs-string">'url'</span>: <span class="hljs-string">'http://payment.example.com'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">60</span>
        },
        {
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'notification-service'</span>,
            <span class="hljs-string">'url'</span>: <span class="hljs-string">'http://notification.example.com'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">15</span>
        }
    ],
    <span class="hljs-string">'retry_policy'</span>: {
        <span class="hljs-string">'max_attempts'</span>: <span class="hljs-number">3</span>,
        <span class="hljs-string">'backoff_seconds'</span>: <span class="hljs-number">5</span>
    }
}

<span class="hljs-comment"># Write to file</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'services.yaml'</span>, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> file:
    yaml.dump(services_config, file, default_flow_style=<span class="hljs-literal">False</span>, sort_keys=<span class="hljs-literal">False</span>)

<span class="hljs-comment"># Read it back</span>
<span class="hljs-keyword">with</span> open(<span class="hljs-string">'services.yaml'</span>, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
    loaded_services = yaml.safe_load(file)

<span class="hljs-comment"># Access list items</span>
<span class="hljs-keyword">for</span> service <span class="hljs-keyword">in</span> loaded_services[<span class="hljs-string">'services'</span>]:
    print(<span class="hljs-string">f"Service: <span class="hljs-subst">{service[<span class="hljs-string">'name'</span>]}</span>, URL: <span class="hljs-subst">{service[<span class="hljs-string">'url'</span>]}</span>"</span>)
</code></pre>
<p>Output:</p>
<pre><code class="lang-plaintext">Service: auth-service, URL: http://auth.example.com
Service: payment-service, URL: http://payment.example.com
Service: notification-service, URL: http://notification.example.com
</code></pre>
<p>This code helps us understand a few key concepts.</p>
<p>We can nest lists and dictionaries freely in our Python data structures. The <code>sort_keys=False</code> parameter preserves the order of keys as we defined them. When we read the YAML back, we can iterate over lists just like any Python list. The data structures in Python match the structures in YAML.</p>
<h2 id="heading-build-a-yaml-config-manager">Build a YAML Config Manager</h2>
<p>Let's put everything together with a practical example. We'll build a simple configuration manager class that handles environment-specific configs (a common need in real projects):</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> yaml
<span class="hljs-keyword">import</span> os

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConfigManager</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, config_dir=<span class="hljs-string">'configs'</span></span>):</span>
        self.config_dir = config_dir
        self.config = {}

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">load_config</span>(<span class="hljs-params">self, environment=<span class="hljs-string">'development'</span></span>):</span>
        <span class="hljs-string">"""Load configuration for a specific environment"""</span>
        config_file = os.path.join(self.config_dir, <span class="hljs-string">f'<span class="hljs-subst">{environment}</span>.yaml'</span>)

        <span class="hljs-keyword">try</span>:
            <span class="hljs-keyword">with</span> open(config_file, <span class="hljs-string">'r'</span>) <span class="hljs-keyword">as</span> file:
                self.config = yaml.safe_load(file)
            print(<span class="hljs-string">f"✓ Loaded configuration for <span class="hljs-subst">{environment}</span>"</span>)
            <span class="hljs-keyword">return</span> self.config
        <span class="hljs-keyword">except</span> FileNotFoundError:
            print(<span class="hljs-string">f"✗ Configuration file not found: <span class="hljs-subst">{config_file}</span>"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
        <span class="hljs-keyword">except</span> yaml.YAMLError <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"✗ Error parsing YAML: <span class="hljs-subst">{e}</span>"</span>)
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get</span>(<span class="hljs-params">self, key_path, default=None</span>):</span>
        <span class="hljs-string">"""Get a configuration value using dot notation"""</span>
        keys = key_path.split(<span class="hljs-string">'.'</span>)
        value = self.config

        <span class="hljs-keyword">for</span> key <span class="hljs-keyword">in</span> keys:
            <span class="hljs-keyword">if</span> isinstance(value, dict) <span class="hljs-keyword">and</span> key <span class="hljs-keyword">in</span> value:
                value = value[key]
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">return</span> default

        <span class="hljs-keyword">return</span> value

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">save_config</span>(<span class="hljs-params">self, environment, config_data</span>):</span>
        <span class="hljs-string">"""Save configuration to a file"""</span>
        config_file = os.path.join(self.config_dir, <span class="hljs-string">f'<span class="hljs-subst">{environment}</span>.yaml'</span>)

        os.makedirs(self.config_dir, exist_ok=<span class="hljs-literal">True</span>)

        <span class="hljs-keyword">with</span> open(config_file, <span class="hljs-string">'w'</span>) <span class="hljs-keyword">as</span> file:
            yaml.dump(config_data, file, default_flow_style=<span class="hljs-literal">False</span>)

        print(<span class="hljs-string">f"✓ Saved configuration for <span class="hljs-subst">{environment}</span>"</span>)
</code></pre>
<p>This <code>ConfigManager</code> class shows you how to build a practical utility:</p>
<ol>
<li><p><strong>Initialization</strong>: We set up a directory for config files.</p>
</li>
<li><p><strong>Loading</strong>: The <code>load_config()</code> method reads environment-specific YAML files with proper error handling.</p>
</li>
<li><p><strong>Accessing data</strong>: The <code>get()</code> method lets you access nested values using dot notation (like <code>'database.host'</code>).</p>
</li>
<li><p><strong>Saving</strong>: The <code>save_config()</code> method writes configuration data to YAML files.</p>
</li>
</ol>
<p>This is the kind of pattern you might actually use in projects. You can extend it further by adding validation, environment variable overrides, or configuration merging. Here’s how you can use the <code>ConfigManager</code> class we’ve coded:</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    <span class="hljs-comment"># Create config manager</span>
    config_mgr = ConfigManager()

    <span class="hljs-comment"># Create a sample development config</span>
    dev_config = {
        <span class="hljs-string">'database'</span>: {
            <span class="hljs-string">'host'</span>: <span class="hljs-string">'localhost'</span>,
            <span class="hljs-string">'port'</span>: <span class="hljs-number">5432</span>,
            <span class="hljs-string">'name'</span>: <span class="hljs-string">'dev_db'</span>
        },
        <span class="hljs-string">'api'</span>: {
            <span class="hljs-string">'base_url'</span>: <span class="hljs-string">'http://localhost:8000'</span>,
            <span class="hljs-string">'timeout'</span>: <span class="hljs-number">30</span>
        }
    }

    <span class="hljs-comment"># Save it</span>
    config_mgr.save_config(<span class="hljs-string">'development'</span>, dev_config)

    <span class="hljs-comment"># Load and use it</span>
    config_mgr.load_config(<span class="hljs-string">'development'</span>)
    print(<span class="hljs-string">f"Database host: <span class="hljs-subst">{config_mgr.get(<span class="hljs-string">'database.host'</span>)}</span>"</span>)
    print(<span class="hljs-string">f"API timeout: <span class="hljs-subst">{config_mgr.get(<span class="hljs-string">'api.timeout'</span>)}</span>"</span>)
</code></pre>
<p>Running the above code should give you the following output:</p>
<pre><code class="lang-plaintext">✓ Saved configuration for development
✓ Loaded configuration for development
Database host: localhost
API timeout: 30
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>YAML is a powerful tool in your developer toolkit. It comes in handy when you’re configuring applications, defining CI/CD pipelines, or working with infrastructure as code.</p>
<p>In this article, you learned how to work with YAML files in Python. You can read configuration files, write data to YAML format, handle lists and nested structures, and build practical utilities like the <code>ConfigManager</code> we coded.</p>
<p>Start small. Try replacing a JSON config file in one of your projects with YAML. You'll quickly appreciate how much more readable it is, and you'll be comfortable working with YAML across the tools and platforms that use it.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Production-Ready DevOps Pipeline with Free Tools ]]>
                </title>
                <description>
                    <![CDATA[ A few months ago, I dove into DevOps, expecting it to be an expensive journey requiring costly tools and infrastructure. But I discovered you can build professional-grade pipelines using entirely free resources. If DevOps feels out of reach because y... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-production-ready-devops-pipeline-with-free-tools/</link>
                <guid isPermaLink="false">680fe1e69418a1165cb184a2</guid>
                
                    <category>
                        <![CDATA[ Devops ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Devops articles ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Kubernetes ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Terraform ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Docker ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Opaluwa Emidowojo ]]>
                </dc:creator>
                <pubDate>Mon, 28 Apr 2025 20:15:34 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745864420670/f36eb4a7-a24e-4d6e-859f-db7249ae0da0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>A few months ago, I dove into DevOps, expecting it to be an expensive journey requiring costly tools and infrastructure. But I discovered you can build professional-grade pipelines using entirely free resources.</p>
<p>If DevOps feels out of reach because you’re also concerned about the cost, don't worry. I’ll guide you step-by-step through creating a production-ready pipeline without spending a dime. Let's get started!</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#introduction">Introduction</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-your-source-control-and-project-structure">How to Set Up Your Source Control and Project Structure</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-your-ci-pipeline-with-github-actions">How to Build Your CI Pipeline with GitHub Actions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-optimize-docker-builds-for-ci">How to Optimize Docker Builds for CI</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-infrastructure-as-code-using-terraform-and-free-cloud-providers">Infrastructure as Code Using Terraform and Free Cloud Providers</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-container-orchestration-on-minimal-resources">How to Set Up Container Orchestration on Minimal Resources</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-free-deployment-pipeline">How to Create a Free Deployment Pipeline</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-build-a-comprehensive-monitoring-system">How to Build a Comprehensive Monitoring System</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-implement-security-testing-and-scanning">How to Implement Security Testing and Scanning</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-performance-optimization-and-scaling">Performance Optimization and Scaling</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-complete-cicd-pipeline-example">Putting it All Together</a></p>
</li>
<li><p><a class="post-section-overview" href="#conclusion">Conclusion</a></p>
</li>
</ol>
<h2 id="heading-prerequisites">🛠 Prerequisites</h2>
<ul>
<li><p><strong>Basic Git knowledge</strong>: Cloning repos, creating branches, committing code, and creating PRs</p>
</li>
<li><p><strong>Familiarity with command line</strong>: For Docker, Terraform, and Kubernetes</p>
</li>
<li><p><strong>Basic understanding of CI/CD</strong>: Continuous integration/delivery concepts and pipelines</p>
</li>
</ul>
<h3 id="heading-accounts-needed">Accounts needed:</h3>
<ul>
<li><p>GitHub account</p>
</li>
<li><p>At least one cloud provider: AWS Free Tier (recommended), Oracle Cloud Free Tier, or Google Cloud/Azure with free credits</p>
</li>
<li><p>Terraform Cloud (free tier) for infrastructure state management</p>
</li>
<li><p>Grafana Cloud (free tier) for monitoring</p>
</li>
<li><p>UptimeRobot (free tier) for external availability checks</p>
</li>
</ul>
<h3 id="heading-tools-to-install-locally">Tools to Install Locally</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Tool</strong></td><td><strong>Purpose</strong></td><td><strong>Installation Link</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Git</td><td>Version control</td><td><a target="_blank" href="https://git-scm.com/downloads"><strong>Install Git</strong></a></td></tr>
<tr>
<td>Docker</td><td>Containerization</td><td><a target="_blank" href="https://docs.docker.com/get-docker/"><strong>Install Docker</strong></a></td></tr>
<tr>
<td>Node.js &amp; npm</td><td>Sample app &amp; builds</td><td><a target="_blank" href="https://nodejs.org/"><strong>Install Node.js</strong></a></td></tr>
<tr>
<td>Terraform</td><td>Infrastructure as Code</td><td><a target="_blank" href="https://www.terraform.io/downloads"><strong>Install Terraform</strong></a></td></tr>
<tr>
<td>kubectl</td><td>Kubernetes CLI</td><td><a target="_blank" href="https://kubernetes.io/docs/tasks/tools/"><strong>Install kubectl</strong></a></td></tr>
<tr>
<td>k3d</td><td>Lightweight Kubernetes</td><td><a target="_blank" href="https://k3d.io/"><strong>Install k3d</strong></a></td></tr>
<tr>
<td>Trivy</td><td>Container security scanning</td><td><a target="_blank" href="https://aquasecurity.github.io/trivy/v0.18.3/"><strong>Install Trivy</strong></a></td></tr>
<tr>
<td>OWASP ZAP</td><td>Web security scanning</td><td><a target="_blank" href="https://www.zaproxy.org/download/"><strong>Install ZAP</strong></a></td></tr>
</tbody>
</table>
</div><p><strong>Optional but Helpful:</strong></p>
<ul>
<li><p><a target="_blank" href="https://code.visualstudio.com/"><strong>VS Code</strong></a> or any good code editor</p>
</li>
<li><p>Postman for testing APIs</p>
</li>
<li><p>Understanding of YAML and Dockerfiles</p>
</li>
</ul>
<h2 id="heading-introduction">Introduction</h2>
<p>When people hear "DevOps," they often picture complex enterprise systems powered by pricey tools and premium cloud services. But the truth is, you don't actually need a massive budget to build a solid, professional-grade DevOps pipeline. The foundations of good DevOps – automation, consistency, security, and visibility – can be built entirely with free tools.</p>
<p>In this guide, you will learn how to build a production-ready DevOps pipeline using zero-cost resources. We will use a simple CRUD (Create, Read, Update, Delete) app with frontend, backend API, and database as our example project to demonstrate every step of the process.</p>
<h2 id="heading-how-to-set-up-your-source-control-and-project-structure">How to Set Up Your Source Control and Project Structure</h2>
<h3 id="heading-1-create-a-well-structured-repository">1. Create a Well-Structured Repository</h3>
<p>A clean repo is the foundation of your pipeline. We will set up:</p>
<ul>
<li><p>Separate folders for <code>frontend</code>, <code>backend</code>, and <code>infrastructure</code></p>
</li>
<li><p>A <code>.github</code> folder to hold workflow configurations</p>
</li>
<li><p>Clear naming conventions and a well-written <code>README.md</code></p>
</li>
</ul>
<p>🛠 <strong>Tip</strong>: Use semantic commit messages and consider adopting <a target="_blank" href="https://www.conventionalcommits.org/"><strong>Conventional Commits</strong></a> for clarity in versioning and changelogs.</p>
<h3 id="heading-2-set-up-branch-protection-without-paid-features">2. Set Up Branch Protection Without Paid Features</h3>
<p>While GitHub's more advanced rules require Pro, you can still:</p>
<ul>
<li><p>Require pull requests before merging</p>
</li>
<li><p>Enable status checks to prevent broken code from landing in <code>main</code></p>
</li>
<li><p>Enforce linear history for cleaner version control</p>
</li>
</ul>
<p>💡 This makes your project safer and more collaborative, without needing GitHub Enterprise.</p>
<h3 id="heading-3-implement-pr-templates-and-automated-checks">3. Implement PR Templates and Automated Checks</h3>
<p>Make your reviews smoother:</p>
<ul>
<li><p>Add a <code>PULL_REQUEST_TEMPLATE.md</code> to guide contributors</p>
</li>
<li><p>Use GitHub Actions (which we'll set up in the next part) for linting, tests, and formatting checks</p>
</li>
</ul>
<p>✨ These tiny improvements add polish and professionalism.</p>
<h3 id="heading-4-configure-github-issue-templates-and-project-boards">4. Configure GitHub Issue Templates and Project Boards</h3>
<p>Even solo developers benefit from issue tracking:</p>
<ul>
<li><p>Add issue templates for bugs and features</p>
</li>
<li><p>Use GitHub Projects to manage work with a Kanban board, all free and native to GitHub</p>
</li>
</ul>
<p>📌 <strong>Bonus</strong>: This setup lays the groundwork for GitOps practices later on.</p>
<h3 id="heading-5-advanced-technique-set-up-custom-validation-scripts-as-pre-commit-hooks">5. Advanced Technique: Set Up Custom Validation Scripts as Pre-Commit Hooks</h3>
<p>Before code ever hits GitHub, you can catch issues locally with Git hooks. Using a tool like <a target="_blank" href="https://typicode.github.io/husky/"><strong>Husky</strong></a> or <a target="_blank" href="https://pre-commit.com/"><strong>pre-commit</strong></a>, you can:</p>
<ul>
<li><p>Lint code before it's committed</p>
</li>
<li><p>Run tests or formatters automatically</p>
</li>
<li><p>Prevent secrets from being accidentally committed</p>
</li>
</ul>
<pre><code class="lang-json"><span class="hljs-comment">// Initialize Husky and install needed dependencies</span>
<span class="hljs-comment">// Then add a pre-commit hook that runs tests before allowing the commit</span>
npx husky-init &amp;&amp; npm install
npx husky add .husky/pre-commit <span class="hljs-string">"npm test"</span>
</code></pre>
<h3 id="heading-6-sample-crud-app-setup"><strong>6. Sample CRUD App Setup:</strong></h3>
<p>Our CRUD app manages users (create, read, update, delete). Below is the minimal code with comments to explain each part:</p>
<p><strong>Backend</strong> <code>(backend/)</code>:</p>
<pre><code class="lang-json"><span class="hljs-comment">// backend/package.json</span>
{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"crud-backend"</span>, <span class="hljs-comment">// Name of the backend project</span>
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>, <span class="hljs-comment">// Version for tracking changes</span>
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node index.js"</span>, <span class="hljs-comment">// Runs the server</span>
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo 'Add tests here'"</span>, <span class="hljs-comment">// Placeholder for tests (update with Jest later)</span>
    <span class="hljs-attr">"lint"</span>: <span class="hljs-string">"eslint ."</span> <span class="hljs-comment">// Checks code style with ESLint</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"express"</span>: <span class="hljs-string">"^4.17.1"</span>, <span class="hljs-comment">// Web framework for API endpoints</span>
    <span class="hljs-attr">"pg"</span>: <span class="hljs-string">"^8.7.3"</span> <span class="hljs-comment">// PostgreSQL client to connect to the database</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"eslint"</span>: <span class="hljs-string">"^8.0.0"</span> <span class="hljs-comment">// Linting tool for code quality</span>
  }
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">// backend/index.js</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>); <span class="hljs-comment">// Import Express for building the API</span>
<span class="hljs-keyword">const</span> { Pool } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'pg'</span>); <span class="hljs-comment">// Import PostgreSQL client</span>
<span class="hljs-keyword">const</span> app = express(); <span class="hljs-comment">// Create an Express app</span>
app.use(express.json()); <span class="hljs-comment">// Parse JSON request bodies</span>

<span class="hljs-comment">// Connect to PostgreSQL using DATABASE_URL from environment variables</span>
<span class="hljs-keyword">const</span> pool = <span class="hljs-keyword">new</span> Pool({ <span class="hljs-attr">connectionString</span>: process.env.DATABASE_URL });

<span class="hljs-comment">// Health check endpoint for Kubernetes probes and monitoring</span>
app.get(<span class="hljs-string">'/healthz'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> res.json({ <span class="hljs-attr">status</span>: <span class="hljs-string">'ok'</span> }));

<span class="hljs-comment">// Get all users from the database</span>
app.get(<span class="hljs-string">'/users'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> pool.query(<span class="hljs-string">'SELECT * FROM users'</span>); <span class="hljs-comment">// Query the users table</span>
  res.json(rows); <span class="hljs-comment">// Send users as JSON</span>
});

<span class="hljs-comment">// Add a new user to the database</span>
app.post(<span class="hljs-string">'/users'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> { name } = req.body; <span class="hljs-comment">// Get name from request body</span>
  <span class="hljs-comment">// Insert user and return the new record</span>
  <span class="hljs-keyword">const</span> { rows } = <span class="hljs-keyword">await</span> pool.query(<span class="hljs-string">'INSERT INTO users(name) VALUES($1) RETURNING *'</span>, [name]);
  res.json(rows[<span class="hljs-number">0</span>]); <span class="hljs-comment">// Send the new user as JSON</span>
});

<span class="hljs-comment">// Start the server on port 3000</span>
app.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Backend running on port 3000'</span>));
</code></pre>
<p><strong>Frontend</strong> <code>(frontend/)</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/package.json</span>
{
  <span class="hljs-string">"name"</span>: <span class="hljs-string">"crud-frontend"</span>, <span class="hljs-comment">// Name of the frontend project</span>
  <span class="hljs-string">"version"</span>: <span class="hljs-string">"1.0.0"</span>, <span class="hljs-comment">// Version for tracking changes</span>
  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"react-scripts start"</span>, <span class="hljs-comment">// Runs the dev server</span>
    <span class="hljs-string">"build"</span>: <span class="hljs-string">"react-scripts build"</span>, <span class="hljs-comment">// Builds for production</span>
    <span class="hljs-string">"test"</span>: <span class="hljs-string">"react-scripts test"</span>, <span class="hljs-comment">// Runs tests (placeholder for Jest)</span>
    <span class="hljs-string">"lint"</span>: <span class="hljs-string">"eslint ."</span> <span class="hljs-comment">// Checks code style with ESLint</span>
  },
  <span class="hljs-string">"dependencies"</span>: {
    <span class="hljs-string">"react"</span>: <span class="hljs-string">"^17.0.2"</span>, <span class="hljs-comment">// Core React library</span>
    <span class="hljs-string">"react-dom"</span>: <span class="hljs-string">"^17.0.2"</span>, <span class="hljs-comment">// Renders React to the DOM</span>
    <span class="hljs-string">"react-scripts"</span>: <span class="hljs-string">"^4.0.3"</span>, <span class="hljs-comment">// Scripts for React development</span>
    <span class="hljs-string">"axios"</span>: <span class="hljs-string">"^0.24.0"</span> <span class="hljs-comment">// HTTP client for API calls</span>
  },
  <span class="hljs-string">"devDependencies"</span>: {
    <span class="hljs-string">"eslint"</span>: <span class="hljs-string">"^8.0.0"</span> <span class="hljs-comment">// Linting tool for code quality</span>
  }
}
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-comment">// frontend/src/App.js</span>
<span class="hljs-keyword">import</span> React, { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>; <span class="hljs-comment">// Import React and hooks</span>
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>; <span class="hljs-comment">// Import Axios for API requests</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// State for storing users fetched from the backend</span>
  <span class="hljs-keyword">const</span> [users, setUsers] = useState([]);
  <span class="hljs-comment">// State for the input field to add a new user</span>
  <span class="hljs-keyword">const</span> [name, setName] = useState(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// Fetch users when the component mounts</span>
  useEffect(<span class="hljs-function">() =&gt;</span> {
    axios.get(<span class="hljs-string">'http://localhost:3000/users'</span>).then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> setUsers(res.data));
  }, []); <span class="hljs-comment">// Empty array means run once on mount</span>

  <span class="hljs-comment">// Add a new user via the API</span>
  <span class="hljs-keyword">const</span> addUser = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">'http://localhost:3000/users'</span>, { name }); <span class="hljs-comment">// Post new user</span>
    setUsers([...users, res.data]); <span class="hljs-comment">// Update users list</span>
    setName(<span class="hljs-string">''</span>); <span class="hljs-comment">// Clear input field</span>
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Users<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      {/* Input for new user name */}
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{name}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{e</span> =&gt;</span> setName(e.target.value)} /&gt;
      {/* Button to add user */}
      <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{addUser}</span>&gt;</span>Add User<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
      {/* List all users */}
      <span class="hljs-tag">&lt;<span class="hljs-name">ul</span>&gt;</span>{users.map(user =&gt; <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>{user.name}<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>)}<span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App; <span class="hljs-comment">// Export the component</span>
</code></pre>
<p><strong>Database Setup</strong>:</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">-- infra/db.sql</span>
<span class="hljs-comment">-- Create a table to store users</span>
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> users (
  id <span class="hljs-type">SERIAL</span> <span class="hljs-keyword">PRIMARY KEY</span>, <span class="hljs-comment">-- Auto-incrementing ID</span>
  <span class="hljs-type">name</span> <span class="hljs-type">VARCHAR</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-keyword">NULL</span> <span class="hljs-comment">-- User name, required</span>
);
</code></pre>
<pre><code class="lang-javascript">crud-app/
├── backend/
│   ├── package.json
│   └── index.js
├── frontend/
│   ├── package.json
│   └── src/App.js
├── infra/
│   └── db.sql
├── .github/
│   └── workflows/
└── README.md
</code></pre>
<p>This app provides a <code>/users</code> endpoint (GET/POST) and a frontend to list/add users, stored in PostgreSQL. The <code>/healthz</code> endpoint supports monitoring. Save this code in your repo to follow the pipeline steps.</p>
<h2 id="heading-how-to-build-your-ci-pipeline-with-github-actions">How to Build Your CI Pipeline with GitHub Actions</h2>
<h3 id="heading-1-set-up-your-first-github-actions-workflow">1. Set Up Your First GitHub Actions Workflow</h3>
<p>First, let’s create a basic workflow that automatically builds, tests, and lints your app every time you push code or open a pull request. This ensures your app stays healthy and any issues are caught early.</p>
<p>Create a file at <code>.github/workflows/ci.yml</code> and add the following:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># CI workflow to build, test, and lint the CRUD app on push or pull request</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CI</span> <span class="hljs-string">Pipeline</span>
<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>] <span class="hljs-comment"># Trigger on pushes to main branch</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span> [<span class="hljs-string">main</span>] <span class="hljs-comment"># Trigger on PRs to main branch</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span> <span class="hljs-comment"># Use GitHub's free Linux runner</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span> <span class="hljs-comment"># Check out the repository code</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">Node.js</span> <span class="hljs-comment"># Install Node.js environment</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">'18'</span> <span class="hljs-comment"># Use Node.js 18 for consistency</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">dependencies</span> <span class="hljs-comment"># Cache node_modules to speed up builds</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">path:</span> <span class="hljs-string">~/.npm</span> <span class="hljs-comment"># Cache npm’s global cache</span>
          <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-${{</span> <span class="hljs-string">hashFiles('**/package-lock.json')</span> <span class="hljs-string">}}</span> <span class="hljs-comment"># Key based on OS and package-lock.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span> <span class="hljs-comment"># Install dependencies reliably using package-lock.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span> <span class="hljs-comment"># Run tests defined in package.json</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">lint</span> <span class="hljs-comment"># Run ESLint to ensure code quality</span>
</code></pre>
<p>This workflow automatically runs on every push and pull request to the <code>main</code> branch. It installs dependencies, runs tests, and performs code linting, with dependency caching to make builds faster over time.</p>
<p><strong>Common Issues and Fixes</strong>:</p>
<ul>
<li><p><strong>“Secret not found”</strong>: Ensure <code>AWS_ACCESS_KEY_ID</code> is in repository secrets (Settings → Secrets).</p>
</li>
<li><p><strong>Tests fail</strong>: Check <code>test/users.test.js</code> for database connectivity.</p>
</li>
</ul>
<h4 id="heading-understanding-github-actions-free-tier-limits">Understanding GitHub Actions' Free Tier Limits</h4>
<p>Before building more workflows, it is important to know what GitHub offers for free.</p>
<p>If you are working on private repositories, you get 2,000 free minutes per month. For public repositories, you get unlimited minutes.</p>
<p>To avoid hitting limits quickly:</p>
<ul>
<li><p>Cache your dependencies to cut down install times.</p>
</li>
<li><p>Only trigger workflows on meaningful branches (like <code>main</code> or <code>release</code>).</p>
</li>
<li><p>Skip unnecessary steps when you can.</p>
</li>
</ul>
<h3 id="heading-2-creating-a-multi-stage-build-pipeline">2. Creating a Multi-Stage Build Pipeline</h3>
<p>As your app grows, it is better to split your CI pipeline into clear stages like <strong>install</strong>, <strong>test</strong>, and <strong>lint</strong>. This structure makes workflows easier to maintain and speeds things up, because some jobs can run in parallel.</p>
<p>Here’s how you can split the work into multiple jobs for better clarity:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">install:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>  <span class="hljs-comment"># Clean install of dependencies</span>

  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># This job depends on the install job finishing</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>  <span class="hljs-comment"># Run test suite</span>

  <span class="hljs-attr">lint:</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># This job also depends on install but runs in parallel with test</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">lint</span>  <span class="hljs-comment"># Run linting checks</span>
</code></pre>
<p>By breaking the pipeline into stages, you can quickly spot which step fails, and your test and lint jobs can run at the same time after dependencies are installed.</p>
<h3 id="heading-3-implement-matrix-builds-for-cross-environment-testing">3. Implement Matrix Builds for Cross-Environment Testing</h3>
<p>When you want your app to work across different Node.js versions or databases, matrix builds are your best bet. They let you test across multiple environments in parallel, without duplicating code.</p>
<p>Here’s how you can set up a matrix strategy, to test across multiple environments simultaneously:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">strategy:</span>
      <span class="hljs-attr">matrix:</span>
        <span class="hljs-attr">node-version:</span> [<span class="hljs-number">14.</span><span class="hljs-string">x</span>, <span class="hljs-number">16.</span><span class="hljs-string">x</span>, <span class="hljs-number">18.</span><span class="hljs-string">x</span>]  <span class="hljs-comment"># Test on multiple Node versions</span>
        <span class="hljs-attr">database:</span> [<span class="hljs-string">postgres</span>, <span class="hljs-string">mysql</span>]        <span class="hljs-comment"># Test against different databases</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Use</span> <span class="hljs-string">Node.js</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">${{</span> <span class="hljs-string">matrix.node-version</span> <span class="hljs-string">}}</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>  <span class="hljs-comment"># This will run 6 different test combinations (3 Node versions × 2 databases)</span>
</code></pre>
<p>Matrix builds save time and help you catch environment-specific bugs early.</p>
<h3 id="heading-4-optimize-workflow-with-dependency-caching">4. Optimize Workflow with Dependency Caching</h3>
<p>Every second counts in CI. Dependency caching can help save minutes in your workflow by reusing previously installed packages instead of reinstalling them from scratch every time.</p>
<p>Here’s how to set up smart caching to speed up your builds:</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">node</span> <span class="hljs-string">modules</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v3</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">|</span>  <span class="hljs-comment"># Cache both global npm cache and local node_modules</span>
      <span class="hljs-string">~/.npm</span>
      <span class="hljs-string">node_modules</span>
    <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-${{</span> <span class="hljs-string">hashFiles('**/package-lock.json')</span> <span class="hljs-string">}}</span>  <span class="hljs-comment"># Cache key based on OS and dependencies</span>
    <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span>  <span class="hljs-comment"># Fallback keys if exact match isn't found</span>
      <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-</span>
</code></pre>
<p>This cache setup checks if your dependencies have changed. If not, it restores the cache, making builds significantly faster.</p>
<h2 id="heading-how-to-optimize-docker-builds-for-ci">How to Optimize Docker Builds for CI</h2>
<p>When you're building Docker images in CI, build time can quickly become a bottleneck. Especially if your images are large. Optimizing your Docker builds makes your pipelines much faster, saves bandwidth, and produces smaller, more efficient images ready for deployment.</p>
<p>In this section, I’ll walk through creating a basic Dockerfile, using multi-stage builds, caching layers, and enabling BuildKit for even faster builds.</p>
<h3 id="heading-1-create-a-baseline-dockerfile">1. Create a Baseline Dockerfile</h3>
<p>First, start with a simple Dockerfile that installs your app’s dependencies and runs it. This is what you’ll be optimizing later.</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Simple Dockerfile for a Node.js application</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine  <span class="hljs-comment"># Use Alpine for a smaller base image</span>
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app         <span class="hljs-comment"># Set working directory</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .             <span class="hljs-comment"># Copy all files to container</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci           <span class="hljs-comment"># Install dependencies (clean install)</span></span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"npm"</span>, <span class="hljs-string">"start"</span>] <span class="hljs-comment"># Start the application</span></span>
</code></pre>
<p>Using an Alpine-based Node.js image helps keep your image small from the start.</p>
<h3 id="heading-2-multi-stage-docker-builds">2. Multi-Stage Docker Builds</h3>
<p>Next, let's separate the build process from the production image. Multi-stage builds let you compile or build your app in one stage and only copy over the final product to a clean, smaller image. This keeps production images lean:</p>
<pre><code class="lang-dockerfile"><span class="hljs-comment"># Stage 1: Build the application</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine AS builder
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-keyword">COPY</span><span class="bash"> package*.json ./  <span class="hljs-comment"># Copy package files first for better caching</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci             <span class="hljs-comment"># Install all dependencies</span></span>
<span class="hljs-keyword">COPY</span><span class="bash"> . .               <span class="hljs-comment"># Then copy source code</span></span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm run build      <span class="hljs-comment"># Build the application</span></span>

<span class="hljs-comment"># Stage 2: Production image with minimal footprint</span>
<span class="hljs-keyword">FROM</span> node:<span class="hljs-number">18</span>-alpine
<span class="hljs-keyword">WORKDIR</span><span class="bash"> /app</span>
<span class="hljs-comment"># Only copy built assets and production dependencies</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/dist ./dist</span>
<span class="hljs-keyword">COPY</span><span class="bash"> --from=builder /app/package*.json ./</span>
<span class="hljs-keyword">RUN</span><span class="bash"> npm ci --production  <span class="hljs-comment"># Install only production dependencies</span></span>
<span class="hljs-keyword">CMD</span><span class="bash"> [<span class="hljs-string">"node"</span>, <span class="hljs-string">"dist/server.js"</span>]  <span class="hljs-comment"># Run the built application</span></span>
</code></pre>
<p>This approach keeps your production images lightweight and secure by excluding unnecessary build tools and dev dependencies.</p>
<h3 id="heading-3-optimizing-layer-caching">3. Optimizing Layer Caching</h3>
<p>For even faster builds, order your <code>Dockerfile</code> instructions to maximize layer caching. Copy and install dependencies <em>before</em> copying your full source code.</p>
<p>This way, Docker reuses the cached npm install step if your dependencies haven't changed, even if you edit your app's code:</p>
<ul>
<li><p>First: <code>COPY package*.json ./</code></p>
</li>
<li><p>Then: <code>RUN npm ci</code></p>
</li>
<li><p>Finally: <code>COPY . .</code></p>
</li>
</ul>
<h3 id="heading-4-enable-buildkit-for-faster-builds">4. Enable BuildKit for Faster Builds</h3>
<p>Docker BuildKit is a newer build engine that enables features like better caching, parallel build steps, and overall faster builds.</p>
<p>To enable BuildKit during your CI, run:</p>
<pre><code class="lang-dockerfile">- name: Build Docker image
  <span class="hljs-keyword">run</span><span class="bash">: |</span>
    <span class="hljs-comment"># Enable BuildKit for parallel and more efficient builds</span>
    DOCKER_BUILDKIT=<span class="hljs-number">1</span> docker build -t myapp:latest .
</code></pre>
<p>Turning on BuildKit can significantly speed up complex Docker builds and is highly recommended for all CI pipelines.</p>
<h2 id="heading-infrastructure-as-code-using-terraform-and-free-cloud-providers">Infrastructure as Code Using Terraform and Free Cloud Providers</h2>
<h3 id="heading-why-infrastructure-as-code-iac-matters">Why Infrastructure as Code (IaC) Matters</h3>
<p>When you manage infrastructure manually – that is, clicking around cloud dashboards or setting things up by hand – it’s easy to lose track of what you did and how to repeat it.</p>
<p>Infrastructure as Code (IaC) solves this by letting you define your infrastructure with code, version it just like application code, and track every change over time. This makes your setups easy to replicate across environments (development, staging, production), ensures changes are declarative and auditable, and reduces human error.</p>
<p>Whether you are spinning up a single server or scaling a complex system, IaC lays the foundation for professional-grade infrastructure from day one, letting you automate, document, and grow your environment systematically.</p>
<h3 id="heading-how-to-provision-infrastructure-with-terraform">How to Provision Infrastructure with Terraform</h3>
<h4 id="heading-initialize-a-terraform-project">Initialize a Terraform Project</h4>
<p>First, define the providers and versions you need. Here, we’re using Render’s free cloud hosting service:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Define required providers and versions</span>
<span class="hljs-string">terraform</span> {
  <span class="hljs-string">required_providers</span> {
    <span class="hljs-string">render</span> <span class="hljs-string">=</span> {
      <span class="hljs-string">source</span>  <span class="hljs-string">=</span> <span class="hljs-string">"renderinc/render"</span>  <span class="hljs-comment"># Using Render's free tier</span>
      <span class="hljs-string">version</span> <span class="hljs-string">=</span> <span class="hljs-string">"0.1.0"</span>             <span class="hljs-comment"># Specify provider version for stability</span>
    }
  }
}

<span class="hljs-comment"># Configure the Render provider with authentication</span>
<span class="hljs-string">provider</span> <span class="hljs-string">"render"</span> {
  <span class="hljs-string">api_key</span> <span class="hljs-string">=</span> <span class="hljs-string">var.render_api_key</span>  <span class="hljs-comment"># Store API key as a variable</span>
}
</code></pre>
<p>Then, configure the provider by authenticating with your API key. It is best practice to store secrets like API keys in variables instead of hardcoding them. This setup tells Terraform what platform you’re working with (Render) and how to authenticate to manage resources automatically.</p>
<h4 id="heading-provision-a-web-app-on-render">Provision a Web App on Render</h4>
<p>Next, define the infrastructure you want – in this case, a web service hosted on Render:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Define a web service on Render's free tier</span>
<span class="hljs-string">resource</span> <span class="hljs-string">"render_service"</span> <span class="hljs-string">"web_app"</span> {
  <span class="hljs-string">name</span> <span class="hljs-string">=</span> <span class="hljs-string">"ci-demo-app"</span>                                 <span class="hljs-comment"># Service name</span>
  <span class="hljs-string">type</span> <span class="hljs-string">=</span> <span class="hljs-string">"web_service"</span>                                 <span class="hljs-comment"># Type of service</span>
  <span class="hljs-string">repo</span> <span class="hljs-string">=</span> <span class="hljs-string">"https://github.com/YOUR-USERNAME/YOUR-REPO"</span>  <span class="hljs-comment"># Source repo</span>
  <span class="hljs-string">env</span> <span class="hljs-string">=</span> <span class="hljs-string">"docker"</span>                                       <span class="hljs-comment"># Use Docker environment</span>
  <span class="hljs-string">plan</span> <span class="hljs-string">=</span> <span class="hljs-string">"starter"</span>                                     <span class="hljs-comment"># Free tier plan</span>
  <span class="hljs-string">branch</span> <span class="hljs-string">=</span> <span class="hljs-string">"main"</span>                                      <span class="hljs-comment"># Deploy from main branch</span>
  <span class="hljs-string">build_command</span> <span class="hljs-string">=</span> <span class="hljs-string">"docker build -t app ."</span>              <span class="hljs-comment"># Build command</span>
  <span class="hljs-string">start_command</span> <span class="hljs-string">=</span> <span class="hljs-string">"docker run -p 3000:3000 app"</span>        <span class="hljs-comment"># Start command</span>
  <span class="hljs-string">auto_deploy</span> <span class="hljs-string">=</span> <span class="hljs-literal">true</span>                                   <span class="hljs-comment"># Auto-deploy on commits</span>
}
</code></pre>
<p>This resource block describes exactly how your app should be deployed. Whenever you change this file and reapply, Terraform will update the infrastructure to match.</p>
<h4 id="heading-provision-postgresql-for-free">Provision PostgreSQL for Free</h4>
<p>Most applications need a database, but you don't have to pay for one when you're getting started. Platforms like <a target="_blank" href="https://railway.app/">Railway</a> offer free tiers that are perfect for development and small projects.</p>
<p>You can quickly create a free PostgreSQL instance by signing up on the platform and clicking <strong>"Create New Project"</strong>. At the end, you'll get a <code>DATABASE_URL</code> a connection string that your app will use to talk to the database.</p>
<h4 id="heading-connect-app-to-db">Connect App to DB</h4>
<p>In Render (or whatever platform you're using), set an environment variable called <code>DATABASE_URL</code> and paste in the connection string from your PostgreSQL provider. This lets your application securely access the database without hardcoding credentials into your codebase.</p>
<h4 id="heading-make-it-reproducible">Make it Reproducible</h4>
<p>Once everything is defined, use Terraform to create and apply an infrastructure plan:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Create execution plan and save it to a file</span>
<span class="hljs-string">terraform</span> <span class="hljs-string">plan</span> <span class="hljs-string">-out=infra.tfplan</span>
<span class="hljs-comment"># Apply the saved plan exactly as planned</span>
<span class="hljs-string">terraform</span> <span class="hljs-string">apply</span> <span class="hljs-string">infra.tfplan</span>
</code></pre>
<p>Saving the plan to a file (<code>infra.tfplan</code>) ensures you’re applying exactly what you reviewed, so there will be no surprises.</p>
<p><strong>Common Issues and Fixes</strong>:</p>
<ul>
<li><p><strong>Provider not found</strong>: Run <code>terraform init</code>.</p>
</li>
<li><p><strong>API key error</strong>: Check <code>render_api_key</code> in Terraform Cloud variables.</p>
</li>
</ul>
<h2 id="heading-how-to-set-up-container-orchestration-on-minimal-resources">How to Set Up Container Orchestration on Minimal Resources</h2>
<p>When you're working with limited resources like a laptop, a small server, or a lightweight cloud VM, setting up full Kubernetes can be overwhelming. Instead, you can use <strong>K3d</strong>, a lightweight Kubernetes distribution that runs inside Docker containers. Here's how to set up a minimal, efficient cluster for local development or testing.</p>
<h3 id="heading-1-install-k3d-for-local-kubernetes">1. Install K3d for Local Kubernetes</h3>
<p>First, install K3d. It's a super lightweight way to run Kubernetes clusters inside Docker without needing a heavy setup like Minikube.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Download and install K3d - a lightweight K8s distribution</span>
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash
</code></pre>
<h3 id="heading-2-create-a-lightweight-k3d-cluster">2. Create a Lightweight K3d Cluster</h3>
<p>Once K3d is installed, you can spin up a cluster with minimal nodes to save resources.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Create a minimal K8s cluster with 1 server and 2 agent nodes</span>
k3d cluster create dev-cluster \
  --servers 1 \                        <span class="hljs-comment"># Single server node to minimize resource usage</span>
  --agents 2 \                         <span class="hljs-comment"># Two worker nodes for pod distribution</span>
  --volume /tmp/k3dvol:/tmp/k3dvol \   <span class="hljs-comment"># Mount local volume for persistence</span>
  --port 8080:80@loadbalancer \        <span class="hljs-comment"># Map port 8080 locally to 80 in the cluster</span>
  --api-port 6443                      <span class="hljs-comment"># Set the API port</span>
</code></pre>
<p>This setup gives you a <strong>tiny but real Kubernetes cluster</strong> that is perfect for experimentation.</p>
<h3 id="heading-3-deploy-with-optimized-kubernetes-manifests">3. Deploy with Optimized Kubernetes Manifests</h3>
<p>Now that your cluster is running, you can deploy your app. It's important to define resource requests and limits carefully so your pods don’t consume too much memory or CPU.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Resource-optimized deployment manifest</span>
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp  <span class="hljs-comment"># Name of the deployment</span>
spec:
  replicas: 1   <span class="hljs-comment"># Single replica to save resources</span>
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
        - name: app
          image: myapp:latest
          resources:
            <span class="hljs-comment"># Set minimal resource requests</span>
            requests:
              memory: <span class="hljs-string">"64Mi"</span>   <span class="hljs-comment"># Request only 64MB memory</span>
              cpu: <span class="hljs-string">"50m"</span>       <span class="hljs-comment"># Request only 5% of a CPU core</span>
            <span class="hljs-comment"># Set reasonable limits</span>
            limits:
              memory: <span class="hljs-string">"128Mi"</span>  <span class="hljs-comment"># Limit to 128MB memory</span>
              cpu: <span class="hljs-string">"100m"</span>      <span class="hljs-comment"># Limit to 10% of a CPU core</span>
</code></pre>
<p>This ensures Kubernetes knows how much to allocate and avoid overloading your lightweight environment.</p>
<h3 id="heading-4-set-up-gitops-with-flux">4. Set up GitOps with Flux</h3>
<p>To manage deployments automatically from your GitHub repository, you can set up GitOps using Flux.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># Install Flux CLI</span>
brew install fluxcd/tap/flux

<span class="hljs-comment"># Bootstrap Flux on your cluster connected to your GitHub repository</span>
flux bootstrap github \
  --owner=YOUR_GITHUB_USERNAME \    <span class="hljs-comment"># Your GitHub username</span>
  --repository=YOUR_REPO_NAME \     <span class="hljs-comment"># Repository to store Flux manifests</span>
  --branch=main \                   <span class="hljs-comment"># Branch to use</span>
  --path=clusters/dev-cluster \     <span class="hljs-comment"># Path within repo for cluster configs</span>
  --personal                        <span class="hljs-comment"># Flag for personal account</span>
</code></pre>
<p>Flux watches your repo and applies updates to your cluster, keeping everything declarative and reproducible.</p>
<p><strong>Common Issues and Fixes</strong>:</p>
<ul>
<li><p><strong>Pods crash</strong>: Run <code>kubectl logs pod-name</code> or increase resources.</p>
</li>
<li><p><strong>Flux sync fails</strong>: Check GitHub token permissions.</p>
</li>
</ul>
<h2 id="heading-how-to-create-a-free-deployment-pipeline">How to Create a Free Deployment Pipeline</h2>
<p>Like I said initially, not every project needs expensive infrastructure. If you're just getting started or building side projects, free tiers from cloud providers can cover a lot of ground.</p>
<h3 id="heading-1-understanding-free-tier-limitations">1. Understanding Free Tier Limitations</h3>
<p>Here’s a quick overview of popular cloud free tiers:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Provider</td><td>Free Tier Highlights</td></tr>
</thead>
<tbody>
<tr>
<td>AWS Free Tier</td><td>750 hours/month EC2, 5GB S3, 1M Lambda requests</td></tr>
<tr>
<td>Oracle Cloud Free Tier</td><td>2 always-free compute instances, 30GB storage</td></tr>
<tr>
<td>Google Cloud Free Tier</td><td>1 f1-micro instance, 5GB storage</td></tr>
</tbody>
</table>
</div><p>Knowing these limits helps you stay within budget.</p>
<h3 id="heading-2-set-up-deployment-workflows">2. Set Up Deployment Workflows</h3>
<p>You can automate deployments with GitHub Actions. Here's an example of a deployment workflow to AWS:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># GitHub Action workflow for deploying to AWS</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">AWS</span> <span class="hljs-string">Deployment</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>  <span class="hljs-comment"># Deploy on push to main branch</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>  <span class="hljs-comment"># Check out code</span>

      <span class="hljs-comment"># Set up AWS credentials from GitHub secrets</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">AWS</span> <span class="hljs-string">credentials</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">aws-actions/configure-aws-credentials@v1</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">aws-access-key-id:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_ACCESS_KEY_ID</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">aws-secret-access-key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">secrets.AWS_SECRET_ACCESS_KEY</span> <span class="hljs-string">}}</span>
          <span class="hljs-attr">aws-region:</span> <span class="hljs-string">us-east-1</span>

      <span class="hljs-comment"># Build the Docker image</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Build</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Image</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">docker</span> <span class="hljs-string">build</span> <span class="hljs-string">-t</span> <span class="hljs-string">myapp</span> <span class="hljs-string">.</span>

      <span class="hljs-comment"># Push the image to AWS ECR</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Push</span> <span class="hljs-string">Docker</span> <span class="hljs-string">Image</span> <span class="hljs-string">to</span> <span class="hljs-string">ECR</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Create repository if it doesn't exist (ignoring errors if it does)
          aws ecr create-repository --repository-name myapp || true
</span>
          <span class="hljs-comment"># Login to ECR</span>
          <span class="hljs-string">aws</span> <span class="hljs-string">ecr</span> <span class="hljs-string">get-login-password</span> <span class="hljs-string">|</span> <span class="hljs-string">docker</span> <span class="hljs-string">login</span> <span class="hljs-string">--username</span> <span class="hljs-string">AWS</span> <span class="hljs-string">--password-stdin</span> <span class="hljs-string">&lt;aws_account_id&gt;.dkr.ecr.us-east-1.amazonaws.com</span>

          <span class="hljs-comment"># Tag and push the image</span>
          <span class="hljs-string">docker</span> <span class="hljs-string">tag</span> <span class="hljs-string">myapp:latest</span> <span class="hljs-string">&lt;aws_account_id&gt;.dkr.ecr.us-east-1.amazonaws.com/myapp:latest</span>
          <span class="hljs-string">docker</span> <span class="hljs-string">push</span> <span class="hljs-string">&lt;aws_account_id&gt;.dkr.ecr.us-east-1.amazonaws.com/myapp:latest</span>
</code></pre>
<h3 id="heading-3-implement-zero-downtime-deployments">3. Implement Zero-Downtime Deployments</h3>
<p>Zero downtime is crucial. Kubernetes makes this easy with rolling updates:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Kubernetes deployment configured for zero-downtime updates</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">crud-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>  <span class="hljs-comment"># Multiple replicas for high availability</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">app</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">&lt;docker_registry&gt;/crud-app:latest</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>  <span class="hljs-comment"># Expose container port</span>
</code></pre>
<p>By having multiple replicas, you ensure that some pods stay live during updates.</p>
<h3 id="heading-4-create-cross-cloud-deployment-for-redundancy">4. Create Cross-Cloud Deployment for Redundancy</h3>
<p>If you want better reliability, you can deploy across different clouds in parallel:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Deploy to multiple cloud providers for redundancy</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">Cross-Cloud</span> <span class="hljs-string">Deployment</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-comment"># Deploy to AWS</span>
  <span class="hljs-attr">aws-deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">AWS</span> <span class="hljs-string">Setup</span> <span class="hljs-string">&amp;</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Configure AWS CLI with credentials
          aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          # AWS deployment commands...
</span>
  <span class="hljs-comment"># Deploy to Oracle Cloud in parallel</span>
  <span class="hljs-attr">oracle-deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Oracle</span> <span class="hljs-string">Setup</span> <span class="hljs-string">&amp;</span> <span class="hljs-string">Deploy</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          # Configure Oracle Cloud CLI
          oci setup config
          # Oracle Cloud deployment commands...</span>
</code></pre>
<p>Now if one cloud goes down, the other is still up.</p>
<h3 id="heading-5-implement-automated-rollbacks-with-health-checks">5. Implement Automated Rollbacks with Health Checks</h3>
<p>Set up health checks so Kubernetes can automatically rollback if something goes wrong:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Deployment with health checks for automated rollbacks</span>
<span class="hljs-attr">apiVersion:</span> <span class="hljs-string">apps/v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Deployment</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">crud-app</span>
<span class="hljs-attr">spec:</span>
  <span class="hljs-attr">replicas:</span> <span class="hljs-number">3</span>
  <span class="hljs-attr">selector:</span>
    <span class="hljs-attr">matchLabels:</span>
      <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
  <span class="hljs-attr">template:</span>
    <span class="hljs-attr">metadata:</span>
      <span class="hljs-attr">labels:</span>
        <span class="hljs-attr">app:</span> <span class="hljs-string">crud-app</span>
    <span class="hljs-attr">spec:</span>
      <span class="hljs-attr">containers:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">crud-app</span>
        <span class="hljs-attr">image:</span> <span class="hljs-string">&lt;docker_registry&gt;/crud-app:latest</span>
        <span class="hljs-attr">ports:</span>
        <span class="hljs-bullet">-</span> <span class="hljs-attr">containerPort:</span> <span class="hljs-number">80</span>
        <span class="hljs-comment"># Check if the container is alive</span>
        <span class="hljs-attr">livenessProbe:</span>
          <span class="hljs-attr">httpGet:</span>
            <span class="hljs-attr">path:</span> <span class="hljs-string">/healthz</span>  <span class="hljs-comment"># Health check endpoint</span>
            <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
          <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>  <span class="hljs-comment"># Wait before first check</span>
          <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">10</span>       <span class="hljs-comment"># Check every 10 seconds</span>
        <span class="hljs-comment"># Check if the container is ready to receive traffic</span>
        <span class="hljs-attr">readinessProbe:</span>
          <span class="hljs-attr">httpGet:</span>
            <span class="hljs-attr">path:</span> <span class="hljs-string">/readiness</span>  <span class="hljs-comment"># Readiness check endpoint</span>
            <span class="hljs-attr">port:</span> <span class="hljs-number">80</span>
          <span class="hljs-attr">initialDelaySeconds:</span> <span class="hljs-number">5</span>  <span class="hljs-comment"># Wait before first check</span>
          <span class="hljs-attr">periodSeconds:</span> <span class="hljs-number">10</span>       <span class="hljs-comment"># Check every 10 seconds</span>
</code></pre>
<h2 id="heading-how-to-build-a-comprehensive-monitoring-system">How to Build a Comprehensive Monitoring System</h2>
<p>Even with a small deployment, monitoring is key to spotting issues early. So now, I’ll walk through setting up a comprehensive monitoring system for your application.</p>
<p>You'll learn how to integrate Grafana Cloud for visualizing your metrics, use Prometheus for collecting data, and configure custom alerts to monitor your app's performance. I’ll also cover tracking Service Level Objectives (SLOs) and setting up external monitoring with UptimeRobot to make sure that your endpoints are always available.</p>
<h3 id="heading-1-set-up-grafana-clouds-free-tier">1. Set Up Grafana Cloud's Free Tier</h3>
<p>Create a Grafana Cloud account and connect Prometheus as a data source. They offer generous free usage, which is perfect for small teams.</p>
<h3 id="heading-2-configure-prometheus-for-metrics-collection">2. Configure Prometheus for Metrics Collection</h3>
<p>Prometheus collects metrics from your app.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># prometheus.yml - Basic Prometheus configuration</span>
<span class="hljs-attr">global:</span>
  <span class="hljs-attr">scrape_interval:</span> <span class="hljs-string">15s</span>  <span class="hljs-comment"># Collect metrics every 15 seconds</span>
<span class="hljs-attr">scrape_configs:</span>
  <span class="hljs-bullet">-</span> <span class="hljs-attr">job_name:</span> <span class="hljs-string">'crud-app'</span>  <span class="hljs-comment"># Job name for the crud-app metrics</span>
    <span class="hljs-attr">static_configs:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">targets:</span> [<span class="hljs-string">'localhost:8080'</span>]  <span class="hljs-comment"># Where to collect metrics from</span>
</code></pre>
<p>This scrapes your app every 15 seconds for metrics.</p>
<h3 id="heading-3-create-monitoring-dashboards">3. Create Monitoring Dashboards</h3>
<p>Grafana visualizes Prometheus data. You can create dashboards using queries like:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Calculate average CPU usage rate per instance over 1 minute</span>
<span class="hljs-string">avg(rate(cpu_usage_seconds_total[1m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(instance)</span>
</code></pre>
<p>This calculates average CPU usage over the last minute per instance.</p>
<h3 id="heading-4-write-custom-promql-queries-for-alerts">4. Write Custom PromQL Queries for Alerts</h3>
<p>You can create smart alerts to detect increasing error rates, like the below:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Calculate error rate as a percentage of total requests</span>
<span class="hljs-comment"># Alert when error rate exceeds 5%</span>
<span class="hljs-string">sum(rate(http_requests_total{status=~"5.."}[5m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(service)</span>
  <span class="hljs-string">/</span> 
<span class="hljs-string">sum(rate(http_requests_total[5m]))</span> <span class="hljs-string">by</span> <span class="hljs-string">(service)</span> <span class="hljs-string">&gt;</span> <span class="hljs-number">0.05</span>
</code></pre>
<p>This alerts if more than 5% of your traffic results in errors.</p>
<h3 id="heading-5-implement-slo-tracking-on-a-budget">5. Implement SLO Tracking on a Budget</h3>
<p>You can track Service Level Objectives (SLOs) with Prometheus for free:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Calculate percentage of requests completed under 200ms</span>
<span class="hljs-comment"># Alert when it drops below 99%</span>
<span class="hljs-string">rate(http_request_duration_seconds_bucket{le="0.2"}[5m])</span> 
  <span class="hljs-string">/</span> <span class="hljs-string">rate(http_request_duration_seconds_count[5m])</span> 
<span class="hljs-string">&gt;</span> <span class="hljs-number">0.99</span>
</code></pre>
<p>This tracks if 99% of requests complete in under 200ms.</p>
<h3 id="heading-6-set-up-uptimerobot-for-external-monitoring">6. Set Up UptimeRobot for External Monitoring</h3>
<p>Finally, you can use UptimeRobot to check if your endpoints are reachable externally, and get alerts if anything goes down.</p>
<h2 id="heading-how-to-implement-security-testing-and-scanning">How to Implement Security Testing and Scanning</h2>
<p>Security should be integrated into your development pipeline from the start, not added as an afterthought. In this section, I’ll show you how to implement security testing and scanning at various stages of your workflow.</p>
<p>You’ll use GitHub CodeQL for static code analysis, OWASP ZAP for scanning web vulnerabilities, and Trivy for container image scanning. You’ll also learn how to enforce security thresholds directly in your CI pipeline.</p>
<h3 id="heading-1-enable-github-code-scanning-with-codeql">1. Enable GitHub Code Scanning with CodeQL</h3>
<p>GitHub has built-in code scanning with CodeQL<strong>.</strong> Here’s how to set it up:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># GitHub workflow for CodeQL security scanning</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CodeQL</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>
  <span class="hljs-attr">pull_request:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">analyze:</span>
    <span class="hljs-attr">name:</span> <span class="hljs-string">Analyze</span> <span class="hljs-string">code</span> <span class="hljs-string">with</span> <span class="hljs-string">CodeQL</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-comment"># Initialize the CodeQL scanning tools</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Set</span> <span class="hljs-string">up</span> <span class="hljs-string">CodeQL</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">github/codeql-action/init@v2</span>

      <span class="hljs-comment"># Run the analysis and generate results</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Analyze</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">github/codeql-action/analyze@v2</span>
</code></pre>
<p>This automatically checks your code for security vulnerabilities.</p>
<h3 id="heading-2-integrate-owasp-zap-into-your-ci-pipeline">2. Integrate OWASP ZAP into Your CI Pipeline</h3>
<p>You can also scan your deployed app with OWASP ZAP like this:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Automated security scanning with OWASP ZAP</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">ZAP</span> <span class="hljs-string">Scan</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">zap-scan:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

      <span class="hljs-comment"># Run the ZAP security scan against deployed application</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">ZAP</span> <span class="hljs-string">security</span> <span class="hljs-string">scan</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">zaproxy/action-full-scan@v0.3.0</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">target:</span> <span class="hljs-string">'https://yourapp.com'</span>  <span class="hljs-comment"># URL to scan</span>
</code></pre>
<p>This checks for common web vulnerabilities.</p>
<h3 id="heading-3-set-up-trivy-for-container-vulnerability-scanning">3. Set Up Trivy for Container Vulnerability Scanning</h3>
<p>You can also check your container images for vulnerabilities with Trivy<strong>:</strong></p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Scan Docker images for vulnerabilities using Trivy</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Trivy</span> <span class="hljs-string">vulnerability</span> <span class="hljs-string">scanner</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">aquasecurity/trivy-action@master</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">image-ref:</span> <span class="hljs-string">'crud-app:latest'</span>   <span class="hljs-comment"># Image to scan</span>
    <span class="hljs-attr">format:</span> <span class="hljs-string">'table'</span>             <span class="hljs-comment"># Output format</span>
    <span class="hljs-attr">exit-code:</span> <span class="hljs-string">'1'</span>              <span class="hljs-comment"># Fail the build if vulnerabilities found</span>
    <span class="hljs-attr">ignore-unfixed:</span> <span class="hljs-literal">true</span>        <span class="hljs-comment"># Skip vulnerabilities without fixes</span>
    <span class="hljs-attr">severity:</span> <span class="hljs-string">'CRITICAL,HIGH'</span>   <span class="hljs-comment"># Only alert on critical and high severity</span>
</code></pre>
<p>Your builds will fail if serious issues are found, keeping you safe by default.</p>
<h3 id="heading-4-create-threshold-based-pipeline-failures">4. Create Threshold-Based Pipeline Failures</h3>
<p>You can configure your pipelines to fail automatically if vulnerabilities exceed a set threshold, enforcing strong security practices without manual effort. Here’s how that should look:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Fail the pipeline if critical or high vulnerabilities are found</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">Trivy</span> <span class="hljs-string">vulnerability</span> <span class="hljs-string">scanner</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">aquasecurity/trivy-action@master</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">image-ref:</span> <span class="hljs-string">'crud-app:latest'</span>   <span class="hljs-comment"># Image to scan</span>
    <span class="hljs-attr">format:</span> <span class="hljs-string">'json'</span>              <span class="hljs-comment"># Output as JSON for parsing</span>
    <span class="hljs-attr">exit-code:</span> <span class="hljs-string">'1'</span>              <span class="hljs-comment"># Fail the build if vulnerabilities found</span>
    <span class="hljs-attr">severity:</span> <span class="hljs-string">'CRITICAL,HIGH'</span>   <span class="hljs-comment"># Check for critical and high severity issues</span>
    <span class="hljs-attr">ignore-unfixed:</span> <span class="hljs-literal">true</span>        <span class="hljs-comment"># Skip vulnerabilities without fixes</span>
</code></pre>
<p>This forces a no-compromise security posture – that is, if critical or high vulnerabilities are detected, the build stops immediately.</p>
<h3 id="heading-5-implement-custom-security-checks">5. Implement Custom Security Checks</h3>
<p>Sometimes you need to go beyond automated scanners. Here's a basic example of a custom security check you can add to your pipeline:</p>
<pre><code class="lang-yaml"><span class="hljs-comment">#!/bin/bash</span>

<span class="hljs-comment"># Custom script to check for hard-coded secrets in source code</span>
<span class="hljs-comment"># Check for hard-coded API keys in source files</span>
<span class="hljs-string">if</span> <span class="hljs-string">grep</span> <span class="hljs-string">-r</span> <span class="hljs-string">"API_KEY"</span> <span class="hljs-string">./src;</span> <span class="hljs-string">then</span>
  <span class="hljs-string">echo</span> <span class="hljs-string">"Security issue: Found hard-coded API keys."</span>
  <span class="hljs-string">exit</span> <span class="hljs-number">1</span>  <span class="hljs-comment"># Fail the build</span>
<span class="hljs-string">else</span>
  <span class="hljs-string">echo</span> <span class="hljs-string">"No hard-coded API keys found."</span>
<span class="hljs-string">fi</span>
</code></pre>
<p>You can extend this script to scan for patterns like private keys, passwords, or other sensitive information, helping catch issues before they ever reach production.</p>
<h2 id="heading-performance-optimization-and-scaling">Performance Optimization and Scaling</h2>
<p>Optimizing early saves you pain later. Here’s how to make your pipelines faster, smarter, and more scalable:</p>
<h3 id="heading-1-measure-pipeline-execution-times">1. Measure Pipeline Execution Times</h3>
<p>Understanding how long each step takes is the first step to improving it:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># Record the start time</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Start</span> <span class="hljs-string">timer</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"Start time: $(date)"</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-comment"># Record the end time to calculate duration</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">End</span> <span class="hljs-string">timer</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"End time: $(date)"</span>
</code></pre>
<p>Later, you can automate time tracking for full reports and alerts.</p>
<h3 id="heading-2-implement-parallelization-strategies">2. Implement Parallelization Strategies</h3>
<p>Split your jobs smartly to save time:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-comment"># First job to install dependencies</span>
  <span class="hljs-attr">install:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">ci</span>

  <span class="hljs-comment"># Run tests in parallel with linting</span>
  <span class="hljs-attr">test:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># Depends on install job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">test</span>

  <span class="hljs-comment"># Run linting in parallel with tests</span>
  <span class="hljs-attr">lint:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">install</span>  <span class="hljs-comment"># Also depends on install job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">run</span> <span class="hljs-string">lint</span>
</code></pre>
<p>Result: Testing and linting run in parallel after installing dependencies, cutting pipeline time significantly.</p>
<h3 id="heading-3-set-up-distributed-caching">3. Set Up Distributed Caching</h3>
<p>Caching saves your workflow from repeating expensive tasks:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Cache dependencies to speed up builds</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Cache</span> <span class="hljs-string">node</span> <span class="hljs-string">modules</span>
  <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/cache@v3</span>
  <span class="hljs-attr">with:</span>
    <span class="hljs-attr">path:</span> <span class="hljs-string">|
      ~/.npm           # Cache global npm cache
      node_modules     # Cache local dependencies
</span>    <span class="hljs-attr">key:</span> <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-${{</span> <span class="hljs-string">hashFiles('**/package-lock.json')</span> <span class="hljs-string">}}</span>  <span class="hljs-comment"># Key based on OS and dependency hash</span>
    <span class="hljs-attr">restore-keys:</span> <span class="hljs-string">|</span>    <span class="hljs-comment"># Fallback keys if exact match isn't found</span>
      <span class="hljs-string">${{</span> <span class="hljs-string">runner.os</span> <span class="hljs-string">}}-node-</span>
</code></pre>
<p><strong>Tip:</strong> Also cache build artifacts, Docker layers, and Terraform plans when possible.</p>
<h3 id="heading-4-create-performance-benchmarks">4. Create Performance Benchmarks</h3>
<p>Track your build times over time with benchmarks:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-comment"># Store the start time as an environment variable</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Start</span> <span class="hljs-string">timer</span>
        <span class="hljs-attr">id:</span> <span class="hljs-string">start_time</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"start_time=$(date +%s)"</span> <span class="hljs-string">&gt;&gt;</span> <span class="hljs-string">$GITHUB_ENV</span>

      <span class="hljs-bullet">-</span> <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>

      <span class="hljs-comment"># Calculate and display the elapsed time</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">End</span> <span class="hljs-string">timer</span> <span class="hljs-string">and</span> <span class="hljs-string">calculate</span> <span class="hljs-string">elapsed</span> <span class="hljs-string">time</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">|
          end_time=$(date +%s)
          elapsed_time=$((end_time - ${{ env.start_time }}))
          echo "Build time: $elapsed_time seconds"</span>
</code></pre>
<p>With benchmarks in place, you can monitor regressions and trigger optimizations automatically.</p>
<h3 id="heading-5-how-to-plan-for-growth-beyond-free-tiers">5. How to Plan for Growth Beyond Free Tiers</h3>
<ul>
<li><p><strong>Understand cloud pricing structures:</strong> AWS, Azure, GCP all offer generous free tiers, but know the limits to avoid surprise bills. <em>(I have been there and it wasn’t pretty.)</em></p>
</li>
<li><p><strong>Consider scaling to more advanced CI/CD tools:</strong> Jenkins, CircleCI, GitLab can offer better performance or self-hosted control as you grow.</p>
</li>
<li><p><strong>Automate resource provisioning:</strong> Use Infrastructure as Code (IaC) with Terraform, Pulumi, or AWS CDK to dynamically scale your infrastructure when your team or traffic grows.</p>
</li>
</ul>
<h2 id="heading-complete-cicd-pipeline-example">Complete CI/CD Pipeline Example</h2>
<p>Here’s a full example tying everything together:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Complete end-to-end CI/CD pipeline</span>
<span class="hljs-attr">name:</span> <span class="hljs-string">CI/CD</span> <span class="hljs-string">Pipeline</span>

<span class="hljs-attr">on:</span>
  <span class="hljs-attr">push:</span>
    <span class="hljs-attr">branches:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">main</span>

<span class="hljs-attr">jobs:</span>
  <span class="hljs-comment"># Initial setup job</span>
  <span class="hljs-attr">setup:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Checkout</span> <span class="hljs-string">code</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/checkout@v3</span>

  <span class="hljs-comment"># Build and test job</span>
  <span class="hljs-attr">build:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">setup</span>  <span class="hljs-comment"># Depends on setup job</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Node.js</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">actions/setup-node@v3</span>
        <span class="hljs-attr">with:</span>
          <span class="hljs-attr">node-version:</span> <span class="hljs-string">'16'</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Install</span> <span class="hljs-string">dependencies</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npm</span> <span class="hljs-string">install</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Run</span> <span class="hljs-string">security</span> <span class="hljs-string">scan</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">npx</span> <span class="hljs-string">eslint</span> <span class="hljs-string">.</span>  <span class="hljs-comment"># Run ESLint for security rules</span>

  <span class="hljs-comment"># Deploy to Kubernetes job</span>
  <span class="hljs-attr">deploy:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">build</span>  <span class="hljs-comment"># Depends on successful build</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">K3d</span> <span class="hljs-string">cluster</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">k3d</span> <span class="hljs-string">cluster</span> <span class="hljs-string">create</span> <span class="hljs-string">dev-cluster</span> <span class="hljs-string">--servers</span> <span class="hljs-number">1</span> <span class="hljs-string">--agents</span> <span class="hljs-number">2</span> <span class="hljs-string">--port</span> <span class="hljs-number">8080</span><span class="hljs-string">:80@loadbalancer</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Apply</span> <span class="hljs-string">Kubernetes</span> <span class="hljs-string">manifests</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">k8s/</span>  <span class="hljs-comment"># Apply all K8s manifests in the k8s directory</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Deploy</span> <span class="hljs-string">app</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">kubectl</span> <span class="hljs-string">rollout</span> <span class="hljs-string">restart</span> <span class="hljs-string">deployment/webapp</span>  <span class="hljs-comment"># Restart deployment for zero-downtime update</span>

  <span class="hljs-comment"># Infrastructure provisioning job</span>
  <span class="hljs-attr">terraform:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">ubuntu-latest</span>
    <span class="hljs-attr">needs:</span> <span class="hljs-string">deploy</span>  <span class="hljs-comment"># Run after deployment</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Setup</span> <span class="hljs-string">Terraform</span>
        <span class="hljs-attr">uses:</span> <span class="hljs-string">hashicorp/setup-terraform@v2</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Terraform</span> <span class="hljs-string">Init</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">terraform</span> <span class="hljs-string">init</span>  <span class="hljs-comment"># Initialize Terraform</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Terraform</span> <span class="hljs-string">Apply</span>
        <span class="hljs-attr">run:</span> <span class="hljs-string">terraform</span> <span class="hljs-string">apply</span> <span class="hljs-string">-auto-approve</span>  <span class="hljs-comment"># Apply infrastructure changes automatically</span>
</code></pre>
<h4 id="heading-runbook-failed-deployment"><strong>Runbook: Failed Deployment:</strong></h4>
<p><strong>Issue</strong>: Pods fail due to resource limits (for example, OOMKilled, CrashLoopBackOff).<br><strong>Fix</strong>:</p>
<pre><code class="lang-yaml">  <span class="hljs-string">kubectl</span> <span class="hljs-string">top</span> <span class="hljs-string">pod</span>
  <span class="hljs-string">kubectl</span> <span class="hljs-string">edit</span> <span class="hljs-string">deployment</span> <span class="hljs-string">crud-app</span>
  <span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">deployment.yaml</span>
  <span class="hljs-string">kubectl</span> <span class="hljs-string">rollout</span> <span class="hljs-string">status</span> <span class="hljs-string">deployment/crud-app</span>
</code></pre>
<p><strong>Tip:</strong> Set realistic resource requests and limits early, it'll save you debugging time later.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By following along with this tutorial, you now know how to build a production-ready DevOps pipeline using free tools:</p>
<ul>
<li><p><strong>CI/CD</strong>: GitHub Actions for testing, linting, and building.</p>
</li>
<li><p><strong>Infrastructure</strong>: Terraform for AWS/Render and PostgreSQL setup.</p>
</li>
<li><p><strong>Orchestration</strong>: K3d for local Kubernetes.</p>
</li>
<li><p><strong>Monitoring</strong>: Grafana, Prometheus, UptimeRobot.</p>
</li>
<li><p><strong>Security</strong>: CodeQL, OWASP ZAP, Trivy for vulnerability scanning.</p>
</li>
</ul>
<p>This pipeline is scalable and secure, and it’s perfect for small projects. As your app grows, you might want to consider paid plans for more resources (for example, AWS larger instances, Grafana unlimited metrics). You can check <a target="_blank" href="https://aws.amazon.com/free/">AWS Free Tier</a>, <a target="_blank" href="https://developer.hashicorp.com/terraform/docs">Terraform Docs</a>, and <a target="_blank" href="https://grafana.com/docs/">Grafana Docs</a> for more learning.</p>
<p><strong>PS:</strong> I’d love to see what you build. Share your pipeline on <a target="_blank" href="https://forum.freecodecamp.org/">FreeCodeCamp’s forum</a> or tag me on X <a target="_blank" href="https://x.com/Emidowojo">@Emidowojo</a> with #DevOpsOnABudget, and tell me about the challenges you faced. You can also connect with me on <a target="_blank" href="https://www.linkedin.com/in/emidowojo/">LinkedIn</a> if you’d like to stay in touch. If you made it to the end of this lengthy article, thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Regular Expressions in YAML File – RegEx in YAML Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ YAML does not have built-in support for regular expressions. But you can still include regex patterns as part of a YAML file's contents, access those patterns, and create a regex out of them. You can do this, for example, with the JavaScript RegExp c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-regular-expressions-in-yaml-file/</link>
                <guid isPermaLink="false">66adf1506f5e63db3fc43620</guid>
                
                    <category>
                        <![CDATA[ Regex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Regular Expressions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Wed, 17 May 2023 14:48:51 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/05/yamlRe.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>YAML does not have built-in support for regular expressions. But you can still include regex patterns as part of a YAML file's contents, access those patterns, and create a regex out of them.</p>
<p>You can do this, for example, with the JavaScript <code>RegExp</code> constructor.</p>
<p>So, in YAML, regular expressions are typically represented as strings, using a specific syntax to define the pattern. For example, a YAML key-value pair that includes a regular expression pattern might look like this:</p>
<pre><code class="lang-bash">example:
pattern: ^[A-Za-z]+$
</code></pre>
<p>In this article, I'll show you how to write regular expressions inside a YAML file and access its entries in a JavaScript file. Let's take a look at what the YAML file is first.</p>
<h2 id="heading-what-well-cover">What We'll Cover</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-a-yaml-file">What is a YAML File?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-write-regular-expressions-in-a-yaml-file">How to Write Regular Expressions in a YAML File</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-import-a-yaml-file-in-javascript-and-use-it">How to Import a YAML File in JavaScript and Use it</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-is-a-yaml-file">What is a YAML File?</h2>
<p>YAML stands for YAML ain't markup language. It is a human and machine-readable data serialization file format. It is often used as configuration files, for data exchange, and for representing structured data in DevOps engineering.</p>
<p>YAML files use indentation and a concise syntax to define data structures such as lists, dictionaries (key-value pairs), and scalars (strings, numbers, booleans).</p>
<p>Each entry in a YAML file can be string, number, or Boolean, and other YAML-specific data types like scalars and lists. Here's a YAML file containing those data types:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># YAML Data Types Example</span>
<span class="hljs-comment"># -----------------------</span>

<span class="hljs-comment"># Scalars</span>
null_example: null           <span class="hljs-comment"># Null Scalar</span>
bool_example: <span class="hljs-literal">true</span>           <span class="hljs-comment"># Boolean Scalar</span>
int_example: 42              <span class="hljs-comment"># Integer Scalar</span>
float_example: 3.14          <span class="hljs-comment"># Float Scalar</span>
str_example: <span class="hljs-string">"Hello, YAML!"</span>  <span class="hljs-comment"># String Scalar</span>

<span class="hljs-comment"># Sequences (Arrays)</span>
seq_example:                 <span class="hljs-comment"># Sequence (Array)</span>
  - Apple
  - Orange
  - Banana

<span class="hljs-comment"># Mappings (Dictionaries)</span>
map_example:                 <span class="hljs-comment"># Mapping (Dictionary)</span>
  key1: value1
  key2: value2
  key3: value3

<span class="hljs-comment"># List (Sequence of Mappings)</span>
list_example:                <span class="hljs-comment"># List of Mappings (Sequence of Dictionaries)</span>
  - name: John
    age: 30
  - name: Jane
    age: 28
  - name: Bob
    age: 35
</code></pre>
<p>You can also put regular expressions right inside a YAML file. And that's what we'll look at next.</p>
<h2 id="heading-how-to-write-regular-expressions-in-a-yaml-file">How to Write Regular Expressions in a YAML File</h2>
<p>You can represent specific values in a YAML file as regular expressions. Below are some validation regex patterns:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># validator.yaml file</span>
password:
  pattern: ^(?!.*[\s])(?=.*[A-Z])(?=.*[a-z])(?=.*\d)[A-Za-z\d@$!%*<span class="hljs-comment">#?&amp;]{8,}$</span>
  description: |
    - At least 8 characters
    - At least one uppercase letter
    - At least one lowercase letter
    - At least one digit
    - Allowed special characters: @$!%*<span class="hljs-comment">#?&amp;</span>

nigerianPhoneNumber:
  pattern: ^(\+?234|0)[789]\d{9}$
  description: |
    - Nigerian phone number format
    - Starts with +234 or 0
    - Followed by 7, 8, or 9
    - Total of 11 digits

email:
  pattern: ^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$
  description: |
    - Valid email address format
    - Example: example@example.com

username:
  pattern: ^[a-zA-Z0-9_-]{3,16}$
  description: |
    - Allowed characters: letters (upper and lower <span class="hljs-keyword">case</span>), numbers, underscore (_), and hyphen (-)
    - Minimum length: 3 characters
    - Maximum length: 16 characters
</code></pre>
<p>You can then import the YAML file into your JavaScript file and do what you want with it – for instance, create regular expressions out of those patterns and use them.</p>
<p>But that process is not straightforward. So that's the next thing you'll learn in this article.</p>
<h2 id="heading-how-to-import-a-yaml-file-in-javascript-and-use-it">How to Import a YAML File in JavaScript and Use it</h2>
<p>If you attempt to import any YAML file into a JavaScript file with the <code>import</code> syntax, like <code>import abc from file.yaml</code>, this is the kind of error you'll get:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/05/reYamlErr.png" alt="reYamlErr" width="600" height="400" loading="lazy"></p>
<p>Instead of doing it that way, you should create a <code>package.json</code> in your project directory by running <code>npm init -y</code> and install the <code>js-yaml</code> package by running <code>npm install js-yaml</code>.</p>
<p>After that, import the <code>fs</code> module of Node.js and the <code>js-yaml</code> package this way:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> yaml = <span class="hljs-built_in">require</span>(<span class="hljs-string">'js-yaml'</span>);
</code></pre>
<p>The next thing you should do is read the <code>validator.yaml</code> file with the <code>readFileSync</code> method of the <code>fs</code> module and parse the YAML file with the <code>load()</code> method:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> yamlData = fs.readFileSync(<span class="hljs-string">'validator.yaml'</span>, <span class="hljs-string">'utf8'</span>);
<span class="hljs-keyword">const</span> parsedData = yaml.load(yamlData);
</code></pre>
<p>All that's left to do is to access any of the patterns, create a RegEx out of it, and use it. This is how I used the password pattern:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> passwordPattern = parsedData.password.pattern;
<span class="hljs-keyword">const</span> pwordValidator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(passwordPattern);

<span class="hljs-keyword">const</span> myPassword = <span class="hljs-string">'reallyStrongPassword21!'</span>;
<span class="hljs-built_in">console</span>.log(pwordValidator.test(myPassword)); <span class="hljs-comment">//true</span>
</code></pre>
<p>Here's how I used the Nigerian phone number validator pattern:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> phonePattern = parsedData.nigerianPhoneNumber.pattern;

phoneValidator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(phonePattern);

<span class="hljs-keyword">const</span> myPhoneNum = <span class="hljs-string">'08133333333'</span>;
<span class="hljs-built_in">console</span>.log(phoneValidator.test(myPhoneNum)); <span class="hljs-comment">//true;</span>
</code></pre>
<p>Here's the full code:</p>
<pre><code class="lang-js"><span class="hljs-comment">// import the fs module to be able to access the YAML file</span>
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

<span class="hljs-comment">// import the YAML package</span>
<span class="hljs-keyword">const</span> yaml = <span class="hljs-built_in">require</span>(<span class="hljs-string">'js-yaml'</span>);

<span class="hljs-comment">// Read the validator.yaml file with the FS module</span>
<span class="hljs-keyword">const</span> yamlData = fs.readFileSync(<span class="hljs-string">'test.yaml'</span>, <span class="hljs-string">'utf8'</span>);

<span class="hljs-comment">// parse the YAML file</span>
<span class="hljs-keyword">const</span> parsedData = yaml.load(yamlData);

<span class="hljs-comment">// Access the password validator pattern from the YAML file</span>
<span class="hljs-keyword">const</span> passwordPattern = parsedData.password.pattern;

<span class="hljs-comment">// Create a regex out of the password pattern</span>
<span class="hljs-keyword">const</span> pwordValidator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(passwordPattern);

<span class="hljs-keyword">const</span> myPassword = <span class="hljs-string">'reallyStrongPassword21!'</span>;
<span class="hljs-built_in">console</span>.log(pwordValidator.test(myPassword)); <span class="hljs-comment">//true</span>

<span class="hljs-comment">// Access the nigeriaPhoneNumber validator pattern from the YAML file</span>
<span class="hljs-keyword">const</span> phonePattern = parsedData.nigerianPhoneNumber.pattern;

<span class="hljs-comment">// Create a regex out of the phonePAttern</span>
phoneValidator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(phonePattern);

<span class="hljs-keyword">const</span> myPhoneNum = <span class="hljs-string">'08133333333'</span>;
<span class="hljs-built_in">console</span>.log(phoneValidator.test(myPhoneNum)); <span class="hljs-comment">//true;</span>

<span class="hljs-comment">// Access the email validator pattern from the YAML file</span>
<span class="hljs-keyword">const</span> emailPattern = parsedData.email.pattern;

<span class="hljs-comment">// Create a regex out of the phonePAttern</span>
emailValidator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(emailPattern);

<span class="hljs-keyword">const</span> emailAddress = <span class="hljs-string">'chris@gmail.com'</span>;
<span class="hljs-built_in">console</span>.log(emailValidator.test(emailAddress)); <span class="hljs-comment">//false;</span>

<span class="hljs-comment">// Access the username validator pattern from the YAML file</span>
<span class="hljs-keyword">const</span> usernamePattern = parsedData.username.pattern;

<span class="hljs-comment">// Create a regex out of the phonePAttern</span>
usernameValidator = <span class="hljs-keyword">new</span> <span class="hljs-built_in">RegExp</span>(usernamePattern);

<span class="hljs-keyword">const</span> username = <span class="hljs-string">'ksound22'</span>;
<span class="hljs-built_in">console</span>.log(usernameValidator.test(username)); <span class="hljs-comment">//false;</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article showed you how to put regular expressions in a YAML file, import it into a JavaScript file with the <code>js-yaml</code> package, and access any of the values in it.</p>
<p>We also looked at how you can create regular expressions out of the patterns in the YAML file and test them with some strings.</p>
<p>Thanks for reading. If you find the article helpful, kindly share it with your friends and family.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ YAML Commenting – How to Add a Multiline Comment in YAML ]]>
                </title>
                <description>
                    <![CDATA[ You can use a YAML file to store data in a format that can be easily read and understood by humans. It is a data serialization language that is often used for configuration files and data transfer between applications. YAML is similar to XML and JSON... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-a-multiline-comment-in-yaml/</link>
                <guid isPermaLink="false">66b0a2bd675b17049e0b4bf2</guid>
                
                    <category>
                        <![CDATA[ data ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ihechikara Abba ]]>
                </dc:creator>
                <pubDate>Mon, 01 May 2023 18:28:28 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/04/vipul-jha-a4X1cdC1QAc-unsplash.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You can use a YAML file to store data in a format that can be easily read and understood by humans. It is a <a target="_blank" href="https://www.freecodecamp.org/news/what-is-yaml-the-yml-file-format/#intro:~:text=Serialization%20is%20a%20process%20where%20one%20application%20or%20service%20that%20has%20different%20data%20structures%20and%20is%20written%20in%20a%20different%20set%20of%20technologies%20can%20transfer%20data%20to%20another%20application%20using%20a%20standard%20format.">data serialization language</a> that is often used for configuration files and data transfer between applications.</p>
<p>YAML is similar to XML and JSON as they can all be used to store data in different formats. The main difference is their syntax. </p>
<p>Here's what XML format looks like: </p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">user</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span>John Doe<span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">phone</span>&gt;</span>00223344<span class="hljs-tag">&lt;/<span class="hljs-name">phone</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">age</span>&gt;</span>80<span class="hljs-tag">&lt;/<span class="hljs-name">age</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">user</span>&gt;</span>
</code></pre>
<p>Here's what JSON format looks like:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"user"</span>: {
    <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>,
    <span class="hljs-attr">"phone"</span>: <span class="hljs-string">"00223344"</span>,
    <span class="hljs-attr">"age"</span>: <span class="hljs-number">80</span>
  }
}
</code></pre>
<p>Here's what YAML format looks like:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">user:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">phone:</span> <span class="hljs-number">00223344</span>
  <span class="hljs-attr">age:</span> <span class="hljs-number">80</span>
</code></pre>
<p>Each of the formats above is used to store data about a user's name, phone number, and age.</p>
<p>You can read more about the features, basic rules, and syntax of YAML, and its differences from JSON and XML in <a target="_blank" href="https://www.freecodecamp.org/news/what-is-yaml-the-yml-file-format/">this article</a>.</p>
<p>In this article, you'll learn about multiline comments in YAML. </p>
<h2 id="heading-how-to-add-a-multiline-comment-in-yaml">How to Add a Multiline Comment in YAML</h2>
<p>You can use comments for various reasons like documenting your code, collaborating with others, stopping a block of code from running, and so on. </p>
<p>You can use the <code>#</code> symbol to create comments in a YAML file. That is:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># The object below represents a user</span>

<span class="hljs-attr">user:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">email:</span> <span class="hljs-string">john.doe@example.com</span>
  <span class="hljs-attr">age:</span> <span class="hljs-number">30</span>
</code></pre>
<p>Unlike some other languages, YAML doesn't have a different format for creating block or multiline comments. </p>
<p>You'll have to use the <code>#</code> symbol on every line the comments spans into. Here's an example: </p>
<pre><code class="lang-yaml"><span class="hljs-comment"># The object below is an example that represents a </span>
<span class="hljs-comment"># user's name, phone number and age</span>

<span class="hljs-attr">user:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">email:</span> <span class="hljs-string">john.doe@example.com</span>
  <span class="hljs-attr">age:</span> <span class="hljs-number">30</span>
</code></pre>
<p>If you remove the <code>#</code> symbol on the second line, the text may still appear as a comment but the YAML parser may interpret it as plain text which may lead to an error. </p>
<p>To be on the safe side, use the <code>#</code> symbol at the start of each comment line. </p>
<h2 id="heading-summary">Summary</h2>
<p>In this article, we talked about YAML. It is mostly used to store and transfer data. </p>
<p>We saw how to create inline and multiline comments. In YAML, the <code>#</code> symbol is used for both inline and multiline comments. </p>
<p>Happy coding! Check out <a target="_blank" href="https://ihechikara.com/">my blog</a> for more programming content.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ What is YAML? The YML File Format ]]>
                </title>
                <description>
                    <![CDATA[ YAML is one of the most popular languages for writing configuration files. In this article, you will learn how YAML compares to XML and JSON - two languages also used for creating configuration files. You will also learn some of the rules and feature... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-yaml-the-yml-file-format/</link>
                <guid isPermaLink="false">66b1e4f698966ccde43c3c66</guid>
                
                    <category>
                        <![CDATA[ markup ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ YAML ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Dionysia Lemonaki ]]>
                </dc:creator>
                <pubDate>Fri, 11 Nov 2022 16:20:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/pexels-christina-morillo-1181675.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>YAML is one of the most popular languages for writing configuration files.</p>
<p>In this article, you will learn how YAML compares to XML and JSON - two languages also used for creating configuration files.</p>
<p>You will also learn some of the rules and features of the language, along with its basic syntax.</p>
<p>Here is what we will cover:</p>
<ol>
<li><a class="post-section-overview" href="#intro">What is YAML?</a></li>
<li><a class="post-section-overview" href="#differences">XML VS JSON VS YAML - What's The Difference?</a><ol>
<li><a class="post-section-overview" href="#xml">XML</a></li>
<li><a class="post-section-overview" href="#json">JSON</a></li>
<li><a class="post-section-overview" href="#yaml">YAML</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#features-and-rules">Features and basic rules of YAML</a><ol>
<li><a class="post-section-overview" href="#create-file">How to create a YAML file</a></li>
<li><a class="post-section-overview" href="#multi-document">Multi-document support in YAML</a></li>
<li><a class="post-section-overview" href="#indentation">Indentation in YAML</a></li>
<li><a class="post-section-overview" href="#tabs">Tabs in YAML</a></li>
<li><a class="post-section-overview" href="#whitespace">Whitespace in YAML</a></li>
<li><a class="post-section-overview" href="#explicit-data-types">Explicit data types in YAML</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#syntax">An introduction to YAML syntax</a><ol>
<li><a class="post-section-overview" href="#scalars">Scalars</a></li>
<li><a class="post-section-overview" href="#collections">Collections</a></li>
</ol>
</li>
</ol>
<h2 id="heading-what-is-yaml">What is YAML? <a></a></h2>
<p>YAML stands for <strong>YAML</strong> <strong>A</strong>in't <strong>M</strong>arkup <strong>L</strong>anguage, but it originally stood for <strong>Y</strong>et <strong>A</strong>nother <strong>M</strong>arkup <strong>L</strong>anguage.</p>
<p>YAML is a human-readable data serialization language, just like XML and JSON.</p>
<p>Serialization is a process where one application or service that has different data structures and is written in a different set of technologies can transfer data to another application using a standard format.</p>
<p>In other words, serialization is about translating, converting, and wrapping up a data structure in another format. </p>
<p>The data in the new format can be stored in a file or transmitted to another application or service over a network.</p>
<p>YAML is a widely used format for writing configuration files for different DevOps tools, programs, and applications because of its human-readable and intuitive syntax.</p>
<h2 id="heading-xml-vs-json-vs-yaml-whats-the-difference">XML VS JSON VS YAML - What's The Difference? <a></a></h2>
<p>XML, JSON, and YAML are all used for creating configuration files and transferring data between applications.  </p>
<p>Each language has its advantages and disadvantages. </p>
<p>Now, let's see some of the characteristics of the three languages. You will also see an example of how the same code is written in each language to demonstrate the high-level differences in their syntax.</p>
<h3 id="heading-xml">XML <a></a></h3>
<p>XML, which stands for Extensible Markup Language, was first introduced in 1996 and was designed for general-purpose use.</p>
<p>XML is a generalized markup language. It offers a structured yet flexible syntax and a defined document schema. This makes it a good choice when working with complex configurations that require a structured format and finer control over schema validation to ensure configurations always have the correct format.</p>
<p>With that said, XML's syntax can be verbose, redundant, and harder to read in comparison with other serialization languages.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Employees</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">Employee</span>&gt;</span> 
        <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span> John Doe <span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">department</span>&gt;</span> Engineering <span class="hljs-tag">&lt;/<span class="hljs-name">department</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">country</span>&gt;</span> USA <span class="hljs-tag">&lt;/<span class="hljs-name">country</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Employee</span>&gt;</span>
     <span class="hljs-tag">&lt;<span class="hljs-name">Employee</span>&gt;</span> 
        <span class="hljs-tag">&lt;<span class="hljs-name">name</span>&gt;</span> Kate Kateson <span class="hljs-tag">&lt;/<span class="hljs-name">name</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">department</span>&gt;</span> IT Support <span class="hljs-tag">&lt;/<span class="hljs-name">department</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">country</span>&gt;</span> United Kingdom <span class="hljs-tag">&lt;/<span class="hljs-name">country</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Employee</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">Employees</span>&gt;</span>
</code></pre>
<h3 id="heading-json">JSON <a></a></h3>
<p>JSON stands for JavaScript Object Notation and has been around since the early 2000s.</p>
<p>JSON was initially inspired by the JavaScript programming language,  but it is not tied to only one language. Instead, it is a language-independent format.</p>
<p>Most modern programming languages have libraries for parsing and generating JSON data.</p>
<p>JSON offers a much more readable, human-friendly, compact, and simple syntax compared to XML. It makes for a great format for storing and transferring information between web applications and servers over a network.</p>
<p>With that said, it may not offer the best support for complex configurations.</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"Employees"</span>: [
    {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"John Doe"</span>,
            <span class="hljs-attr">"department"</span>: <span class="hljs-string">"Engineering"</span>,
            <span class="hljs-attr">"country"</span>: <span class="hljs-string">"USA"</span>
        },

        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Kate Kateson"</span>,
            <span class="hljs-attr">"department"</span>: <span class="hljs-string">"IT support"</span>,
            <span class="hljs-attr">"country"</span>: <span class="hljs-string">"United Kingdom"</span>
        }
    ]
}
</code></pre>
<h3 id="heading-yaml">YAML <a></a></h3>
<p>YAML, originally known as Yet Another Markup Language, was created in 2001 but now stands for YAML Ain't Markup Language.</p>
<p>YAML is an official strict superset of JSON despite looking very different from JSON.  </p>
<p>YAML can do everything that JSON can and more. A valid YAML file can contain JSON, and JSON can transform into YAML.</p>
<p>YAML has the most human-readable, intuitive, and compact syntax for defining configurations compared to XML and JSON.</p>
<p>YAML uses indentation to define structure in the file, which is helpful if you are used to writing Python code and are familiar with the indentation style the language uses. </p>
<p>With that said, if you don't get the indentation and format right, it can lead to validation errors, making it not the friendliest for beginners.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Employees:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">Engineering</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Kate</span> <span class="hljs-string">Kateson</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">IT</span> <span class="hljs-string">support</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">United</span> <span class="hljs-string">Kingdom</span>
</code></pre>
<h2 id="heading-features-and-basic-rules-of-yaml">Features and Basic Rules of YAML <a></a></h2>
<p>Now, let's go over some of the basic rules and features of the language.</p>
<h3 id="heading-how-to-create-a-yaml-file">How to Create a YAML File <a></a></h3>
<p>To create a YAML file, use either the <code>.yaml</code> or <code>.yml</code> file extension.</p>
<h3 id="heading-multi-document-support-in-yaml">Multi-Document Support in YAML <a></a></h3>
<p>Before writing any YAML code, you can add three dashes (<code>---</code>) at the start of the file:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">Employees:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">Engineering</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Kate</span> <span class="hljs-string">Kateson</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">IT</span> <span class="hljs-string">support</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">United</span> <span class="hljs-string">Kingdom</span>
</code></pre>
<p>YAML allows you to have multiple YAML documents in a singe YAML file, making file organization much easier.</p>
<p>Separate each document with three dashes (<code>---</code>):</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">Employees:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">Engineering</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Kate</span> <span class="hljs-string">Kateson</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">IT</span> <span class="hljs-string">support</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">United</span> <span class="hljs-string">Kingdom</span>
<span class="hljs-meta">---</span>
<span class="hljs-attr">Fruit:</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">Oranges</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">Pears</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">Apples</span>
</code></pre>
<p>You can also use three dots (<code>...</code>) to mark the end of the document:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-attr">Employees:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">Engineering</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Kate</span> <span class="hljs-string">Kateson</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">IT</span> <span class="hljs-string">support</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">United</span> <span class="hljs-string">Kingdom</span>
<span class="hljs-string">...</span>
</code></pre>
<h3 id="heading-indentation-in-yaml">Indentation in YAML <a></a></h3>
<p>In YAML, there is an emphasis on indentation and line separation to denote levels and structure in data. The indentation system is quite similar to the one Python uses.</p>
<p>YAML doesn't use symbols such as curly braces, square brackets, or opening or closing tags - just indentation.</p>
<h3 id="heading-tabs-in-yaml">Tabs in YAML <a></a></h3>
<p>YAML doesn't allow you to use any tabs when creating indentation - use spaces instead.</p>
<h3 id="heading-whitespace-in-yaml">Whitespace in YAML <a></a></h3>
<p>Whitespace doesn't matter as long as child elements are indented inside the parent element.</p>
<h3 id="heading-how-to-write-a-comment-in-yaml">How to Write A Comment in YAML <a></a></h3>
<p>To add a comment to comment out a line of code, use the <code>#</code> character:</p>
<pre><code class="lang-yaml"><span class="hljs-meta">---</span>
<span class="hljs-comment"># Employees in my company</span>
<span class="hljs-attr">Employees:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">Engineering</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Kate</span> <span class="hljs-string">Kateson</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">IT</span> <span class="hljs-string">support</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">United</span> <span class="hljs-string">Kingdom</span>
</code></pre>
<h3 id="heading-explicit-data-types-in-yaml">Explicit Data Types in YAML <a></a></h3>
<p>Although YAML auto-detects the data types in a file, you can specify the type of data you want to use.</p>
<p>To explicitly specify the type of data, use the <code>!!</code> symbol and the name of the data type before the value:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># parse this value as a string</span>
<span class="hljs-attr">date:</span> <span class="hljs-type">!!str</span> <span class="hljs-number">2022-11-11</span>

<span class="hljs-comment">## parse this value as a float (it will be 1.0 instead of 1)</span>
<span class="hljs-attr">fave_number:</span> <span class="hljs-type">!!float</span> <span class="hljs-number">1</span>
</code></pre>
<h2 id="heading-an-introduction-to-yaml-syntax">An Introduction to YAML Syntax <a></a></h2>
<h3 id="heading-scalars">Scalars <a></a></h3>
<p>Scalars in YAML are the data on the page - strings, numbers, booleans, and nulls.</p>
<p>Let's see some examples of how to use each one.</p>
<p>In YAML, strings in some cases can be left unquoted, but you can also wrap them in single (<code>' '</code>) or double (<code>" "</code>) quotation marks:</p>
<pre><code class="lang-yaml"><span class="hljs-string">A</span> <span class="hljs-string">string</span> <span class="hljs-string">in</span> <span class="hljs-string">YAML!</span>

<span class="hljs-string">'A string in YAML!'</span>

<span class="hljs-string">"A string in YAML!"</span>
</code></pre>
<p>If you want to write a string that spans across multiple lines and you want to preserve the line breaks, use the pipe symbol (<code>|</code>):</p>
<pre><code class="lang-yaml"><span class="hljs-string">|
 I am message that spans multiple lines
 I go on and on across lines
 and lines
 and more lines</span>
</code></pre>
<p>Make sure that the message is indented!</p>
<p>Alternatively, if you have a string in a YAML file that spans across multiple lines for readability, but you want the parser to interpret it as a single line string, you can use the <code>&gt;</code> character, which will replace each line break with a space:</p>
<pre><code class="lang-yaml"><span class="hljs-string">&gt;
 I am message that spans
 multiple lines
 but I will be parsed
 on one line</span>
</code></pre>
<p>Again, make sure you don't forget to indent the message!</p>
<p>Numbers express numerical data, and in YAML, these include integers (whole numbers), floats (numbers with a decimal point), exponentials, octals, and hexadecimals:</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># integer</span>
<span class="hljs-number">19</span>

<span class="hljs-comment"># float </span>
<span class="hljs-number">8.7</span>

<span class="hljs-comment"># exponential</span>
<span class="hljs-number">4.5e+13</span>

<span class="hljs-comment"># octal </span>
<span class="hljs-string">0o23</span>

<span class="hljs-comment"># hexadecimal</span>
<span class="hljs-number">0xFF</span>
</code></pre>
<p>Booleans in YAML, and other programming languages, have one of two states and are expressed with either <code>true</code> or <code>false</code>.</p>
<p>Words like <code>true</code> and <code>false</code> are keywords in YAML, so don't surround them with quotation marks if you want them interpreted as booleans.</p>
<p>Lastly, Null values are expressed with the keyword <code>null</code> or the tilde character, <code>~</code>.</p>
<h3 id="heading-collections">Collections <a></a></h3>
<p>More often than not, you will not be writing simple scalars in your YAML files - you will be using collections instead.</p>
<p>Collections in YAML can be:</p>
<ul>
<li>Sequences (lists/arrays)</li>
<li>Mappings (dictionaries/hashes)</li>
</ul>
<p>To write a sequence, use a dash (<code>-</code>) followed by a space:</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-string">HTML</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">CSS</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">JavaScript</span>
</code></pre>
<p>Each item in the sequence (list) is placed on a separate line, with a dash in front of the value.</p>
<p>And each item in the list is on the same level.</p>
<p>That said, you can create a nested sequence (remember, use spaces - not tabs - to create the levels of indentation):</p>
<pre><code class="lang-yaml"><span class="hljs-bullet">-</span> <span class="hljs-string">HTML</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">CSS</span>
<span class="hljs-bullet">-</span> <span class="hljs-string">JavaScript</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">React</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">Angular</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">Vue</span>
</code></pre>
<p>In the sequence above, <code>React, Angular and Vue</code> are sub-items of the item <code>JavaScript</code>.</p>
<p>Mappings allow you to list keys with values. Key/value pairs are the building blocks of YAML documents.</p>
<p>Use a colon (<code>:</code>) followed by a space to create key/value pairs:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Employees:</span>
 <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
 <span class="hljs-attr">age:</span> <span class="hljs-number">23</span>
 <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
</code></pre>
<p>In the example above, a name gets assigned to a specific value.</p>
<p>The value <code>John Doe</code> gets mapped (or assigned) to the <code>name</code> key, the value <code>23</code> gets mapped to the <code>age</code> key, and the value <code>USA</code> gets mapped to the <code>country</code> key. Altogether, these create an object.</p>
<p>You can also use a mapping with a sequence.</p>
<p>For example, taking the example sequence from earlier on, here is how you would build a list of <code>frontend_languages</code>:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">frontend_languages:</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">HTML</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">CSS</span>
 <span class="hljs-bullet">-</span> <span class="hljs-string">JavaScript</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">React</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">Angular</span>
  <span class="hljs-bullet">-</span> <span class="hljs-string">Vue</span>
</code></pre>
<p>In the example above, I created a list of <code>frontend_languages</code>, where there are multiple values under the same key, <code>frontend_languages</code>.</p>
<p>Similarly, you can create a list of objects:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">Employees:</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">John</span> <span class="hljs-string">Doe</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">Engineering</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">USA</span>
<span class="hljs-bullet">-</span> <span class="hljs-attr">name:</span> <span class="hljs-string">Kate</span> <span class="hljs-string">Kateson</span>
  <span class="hljs-attr">department:</span> <span class="hljs-string">IT</span> <span class="hljs-string">support</span>
  <span class="hljs-attr">country:</span> <span class="hljs-string">United</span> <span class="hljs-string">Kingdom</span>
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Hopefully, this article was helpful and gave you insight into what YAML is, what the syntax of the language looks like, and how it differs from XML and JSON.</p>
<p>Thank you for reading, and happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
