<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Thisara Dasun on Medium]]></title>
        <description><![CDATA[Stories by Thisara Dasun on Medium]]></description>
        <link>https://medium.com/@thisarad404?source=rss-3a1f95f999cb------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*tFU0KaahNEnxTzw-9W3Tzg.jpeg</url>
            <title>Stories by Thisara Dasun on Medium</title>
            <link>https://medium.com/@thisarad404?source=rss-3a1f95f999cb------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 10 Jul 2026 01:33:39 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@thisarad404/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[The Cloud Compute Cliff: Burstable VMs, CPU Credit Math, and Decoupled Architectures]]></title>
            <link>https://thisarad404.medium.com/the-cloud-compute-cliff-burstable-vms-cpu-credit-math-and-decoupled-architectures-10b0bc45d82e?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/10b0bc45d82e</guid>
            <category><![CDATA[decoupled-architecture]]></category>
            <category><![CDATA[cloud-computing]]></category>
            <category><![CDATA[cpu-credit-math]]></category>
            <category><![CDATA[cpu-utilization]]></category>
            <category><![CDATA[cloud-services]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sun, 21 Jun 2026 00:46:00 GMT</pubDate>
            <atom:updated>2026-06-23T04:40:35.827Z</atom:updated>
            <content:encoded><![CDATA[<h3>Why selecting the cheapest VM instance is an operational hazard, and how to design decoupled, cost-optimized cloud infrastructure.</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MTXzogYBiLYPCaYgg3tWCA.jpeg" /></figure><p><em>Photo by </em><a href="https://pin.it/3jr79MCSb"><strong><em>Pinterest</em></strong></a></p><p>When deploying a new service or database to the cloud, the budget-conscious developer’s default instinct is to scroll straight to the bottom of the virtual machine catalog and select the cheapest option available.</p><p>On Amazon Web Services (AWS), this might be a t3.micro. On Microsoft Azure, a Standard_B1s.</p><p>On paper, these “burstable” instances look like an incredible deal: they cost mere pennies a day, offering plenty of RAM and basic processing power for dev environments or microservices.</p><p>Then, you run a simple database migration, compile some assets, or experience a minor traffic spike. Suddenly, your application slows to a crawl, API responses timeout, and SSH connections freeze.</p><p>You’ve hit the <strong>Compute Cliff</strong>.</p><p>In this guide, we will dive deep into the underlying mechanics of cloud compute tiers. We will map out the mathematical models behind <strong>burstable CPU credits</strong>, explain how <strong>decoupling storage from compute</strong> guarantees durability, and detail how to use <strong>resource governance hierarchies</strong> to prevent silent billing leaks.</p><h3>1. The Mathematics of Burstable Compute</h3><p>Burstable instances (like the <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/burstable-performance-instances.html">AWS T3/T4g family</a> and <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/sizes/b-series-burstable-virtual-machine-sizes">Azure B-series</a>) are designed for workloads that sit idle most of the time but need sudden, short-lived bursts of CPU power.</p><p>Instead of paying for dedicated, continuous CPU access, you operate on a <strong>CPU Credit Economy</strong>.</p><h3>The Mathematical Model</h3><p>Each burstable VM is assigned a <strong>baseline CPU performance</strong> and a <strong>credit accumulation rate</strong> based on its instance size.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CbLrXo6vAxAAmCb-508pYw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OSU8Wk027s0lvw2QiGClGw.png" /></figure><p><strong>Architectural Takeaway:</strong> Use burstable instances only for asynchronous tasks (like message queues, background workers, or staging servers) where a sudden slowdown won’t break the user experience. For critical web APIs or primary databases, always select “General Purpose” (e.g., AWS M-series, Azure D-series) or “Compute Optimized” (e.g., AWS C-series, Azure F-series) instances with dedicated, continuous CPU cores.</p><h3>2. Storage Decoupling: Ephemeral vs. Persistent Block Storage</h3><p>When you spin up a cloud virtual machine, the operating system is installed on a virtual hard drive. In a modern cloud, this drive is almost never physically inside the physical blade hosting your VM. Instead, it is network-attached block storage.</p><h3>Persistent Block Storage</h3><p>Services like <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html">AWS Elastic Block Store (EBS)</a> and <a href="https://learn.microsoft.com/en-us/azure/virtual-machines/managed-disks-overview">Azure Managed Disks</a> decouple storage from compute entirely. This architectural separation provides massive advantages for system durability and disaster recovery:</p><pre>┌─────────────────────┐               ┌───────────────────────┐<br>│     COMPUTE VM      │               │ PERSISTENT STORAGE    │<br>│  (CPU &amp; RAM State)  │               │   (EBS / Managed Disk)│<br>│  Can be destroyed,  │               │                       │<br>│  resized, or moved. │               │   State is preserved  │<br>└──────────┬──────────┘               │   synchronously.      │<br>           │                          └──────────┬────────────┘<br>           │      Network Attachment             │<br>           └─────────────────────────────────────┘</pre><p>If the underlying hardware node hosting your VM fails, the cloud hypervisor can automatically provision a new virtual machine on a healthy host, detach your persistent block volume from the old node, and attach it to the new one. Your data remains perfectly intact and up-to-date.</p><h3>Ephemeral Storage (Instance Store)</h3><p>Conversely, some instances offer local NVMe drives attached directly to the physical host. This is known as <strong>ephemeral storage</strong> or <strong>instance store</strong>.</p><ul><li><strong>Pros:</strong> Extremely low latency and astronomical read/write IOPS (Input/Output Operations Per Second).</li><li><strong>Cons:</strong> If the virtual machine is stopped, deallocated, or fails, <strong>all data on the ephemeral drive is lost forever.</strong></li></ul><p><strong>Architectural Takeaway:</strong> Never store database engines or stateful application configurations on ephemeral drives. Use persistent, network-attached block volumes (like Azure standard LRS/SSD or AWS gp3) for your persistent data, and reserve ephemeral storage strictly for swap files, temp databases, caching layers, and high-speed data processing buffers.</p><h3>3. Resource Governance and the “Orphaned Resource” Tax</h3><p>When teams build and deploy systems incrementally, they often spin up VMs to test architectures, destroy them when finished, and move on.</p><p>However, because compute and storage are decoupled, <strong>deleting a virtual machine does not automatically delete its associated resources.</strong></p><p>If you delete an Azure VM or an AWS EC2 instance without checking your dependency trees, you will leave behind:</p><ul><li>Unattached block storage volumes (charging you per-gigabyte/month).</li><li>Unused Public IP addresses (both AWS and Azure tax you hourly for holding a static public IP without associating it to an active VM).</li><li>Orphaned Network Interfaces (NICs) and security group artifacts.</li></ul><p>This is where <strong>Resource Governance Hierarchies</strong> save budgets.</p><pre>[MANAGEMENT GROUP]<br>              │<br>              ▼<br>         [SUBSCRIPTION]<br>              │<br>              ▼<br>       [RESOURCE GROUP]  &lt;─── Virtual Machine, Disk, Public IP, NIC (Tied Lifecycle)</pre><p>By organizing your applications within logical containers like <a href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/overview">Azure Resource Groups</a> or <a href="https://docs.aws.amazon.com/ARG/latest/userguide/resource-groups.html">AWS Resource Groups</a>, you can tie the lifecycle of related resources together.</p><p>When you tear down a project, you don’t delete the VM — you simply delete the entire <strong>Resource Group</strong>. The cloud provider then triggers a cascading deletion, systematically wiping out the VM, its network cards, its public IPs, and its persistent disks in one clean sweep, ensuring zero orphaned resources on your next monthly invoice.</p><h3>Summary Checklist: Designing Resilient Compute Architectures</h3><p>Before launching your next cloud system into production, ensure your design meets these architectural standards:</p><ul><li>[ ] <strong>Evaluate CPU Profiles:</strong> Do not use burstable instances for production databases. Ensure critical APIs run on general-purpose or compute-optimized tiers.</li><li>[ ] <strong>Decouple Stateful Data:</strong> Store all critical data on persistent block storage volumes (e.g., Azure LRS, AWS gp3). Never write persistent records directly to ephemeral/instance drives.</li><li>[ ] <strong>Monitor Your Credit Banks:</strong> If you use burstable VMs, set up alarms in AWS CloudWatch or Azure Monitor to alert your team if your credit balance ($C(t)$) falls below $20\%$.</li><li>[ ] <strong>Enforce Group Governance:</strong> Organize all resources inside logical containment boundaries (Resource Groups) to manage lifecycles and avoid “zombie resources” billing leaks.</li></ul><p>DOWNLOAD RESOURCES: <a href="https://docs.google.com/document/d/1nkFv9rXbpyPT0anDC9mCn2mPSG6hWuvMk53Lz60WpK4/edit?usp=sharing">https://docs.google.com/document/d/1nkFv9rXbpyPT0anDC9mCn2mPSG6hWuvMk53Lz60WpK4/edit?usp=sharing</a></p><p><em>How does your team optimize its compute spending? Have you ever run into the burstable performance throttling cliff? Share your operational stories in the comments below!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=10b0bc45d82e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Demystifying Cloud Networking: CIDR Math, Stateful Firewalls, and the Security Group vs.]]></title>
            <link>https://thisarad404.medium.com/demystifying-cloud-networking-cidr-math-stateful-firewalls-and-the-security-group-vs-b5fdf72ca7a3?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/b5fdf72ca7a3</guid>
            <category><![CDATA[cidr]]></category>
            <category><![CDATA[azure]]></category>
            <category><![CDATA[aws-nacl]]></category>
            <category><![CDATA[subnetting]]></category>
            <category><![CDATA[aws]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sat, 20 Jun 2026 00:46:01 GMT</pubDate>
            <atom:updated>2026-06-20T00:46:01.142Z</atom:updated>
            <content:encoded><![CDATA[<h3>Demystifying Cloud Networking: CIDR Math, Stateful Firewalls, and the Security Group vs. NACL Showdown</h3><h3>The ultimate multi-cloud guide to subnets, firewall statefulness, and how to prevent silent network blocks in AWS and Azure.</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t4a2NpenTWqEwtQffumHCg.jpeg" /></figure><p><em>Photo by: </em><a href="https://pin.it/654y7y5yV"><strong><em>Pinterest</em></strong></a></p><p>When teams begin migrating infrastructure to public clouds like Amazon Web Services (AWS) or Microsoft Azure, they often treat cloud networking like their old on-premises server closets.</p><p>They write a few firewall rules, spin up a virtual machine, and expect traffic to flow smoothly.</p><p>Then, the troubleshooting tickets start piling up:</p><ul><li><em>“Our application server can’t download package updates.”</em></li><li><em>“We added an inbound HTTP rule, but our health checks are still failing.”</em></li><li><em>“We ran out of IP addresses in our subnet within three weeks.”</em></li></ul><p>In the cloud, networking is entirely software-defined, and security is integrated directly into the fabric of the hypervisor. To build resilient, secure infrastructure, you need to master three fundamental concepts: <strong>CIDR IP arithmetic</strong>, <strong>Firewall Statefulness</strong>, and the <strong>Stateful vs. Stateless boundary</strong>.</p><p>In this guide, we’ll demystify these core concepts, compare AWS and Azure security structures, and map out the mathematics of modern cloud subnetting.</p><h3>1. Cloud Subnetting Math and the “Minus 5” Tax</h3><p>In traditional networking, IP allocation is calculated using standard Classless Inter-Domain Routing (CIDR) notation. An IPv4 address consists of $32$ bits. When you declare a CIDR block with a prefix length of $n$ (for example, /24), you are reserving the first $n$ bits for the network identifier, leaving $32 - n$ bits for host allocation.</p><p>Mathematically, the total number of IP addresses available in a given subnet is:</p><blockquote><strong><em>Total IPs} = 2^{32 — n}</em></strong></blockquote><p>For a standard /24 subnet:</p><blockquote><strong><em>Total IPs = 2^{32–24} = 2⁸ = 256 addresses</em></strong></blockquote><p>For a smaller /28 subnet:</p><blockquote><strong><em>Total IPs = 2^{32–28} = 2⁴ = 16 addresses</em></strong></blockquote><h4>The Cloud Tax: 5 Reserved IPs</h4><p>In on-premises networks, you only lose $2$ IP addresses per subnet: the network address (all host bits set to $0$) and the broadcast address (all host bits set to $1$).</p><p>However, both <a href="https://docs.aws.amazon.com/vpc/latest/userguide/configure-subnets.html">AWS VPC Subnets</a> and <a href="https://learn.microsoft.com/en-us/azure/virtual-network/virtual-networks-overview">Azure Virtual Network Subnets</a> reserve <strong>five</strong> IP addresses per subnet.</p><p>For any cloud subnet of size $n$, the actual number of usable host addresses is:</p><blockquote><strong>Usable Cloud IPs = 2^{32 — n} — 5</strong></blockquote><p>If you provision a /28 subnet expecting $14$ usable addresses for a cluster of microservices, you will find yourself with only $11$ usable IPs:</p><blockquote><strong>Usable IPs = 2^{32–28} — 5 = 16–5 = 11</strong></blockquote><p>The cloud providers claim these five addresses for critical infrastructure operations:</p><ol><li><strong>Host </strong><strong>.0</strong>: Network Address.</li><li><strong>Host </strong><strong>.1</strong>: Default Gateway (VPC/VNet router).</li><li><strong>Host </strong><strong>.2</strong>: DNS Server (e.g., AWS Route 53 resolver or Azure DNS).</li><li><strong>Host </strong><strong>.3</strong>: Future capability / internal system mapping.</li><li><strong>Host </strong><strong>.255</strong>: Network Broadcast Address (even though the cloud does not support traditional IP broadcasting).</li></ol><p><strong>Architectural Takeaway:</strong> Never size your subnets too tightly. If you run out of IP addresses in a subnet, <strong>you cannot resize it.</strong> You must destroy the subnet and recreate it from scratch, which often causes major deployment disruptions.</p><h3>2. Stateful Firewalls: Under the Hood of Security Groups</h3><p>The primary line of defense for a cloud virtual machine is the Security Group (known as an EC2 Security Group in AWS, and a Network Security Group or NSG in Azure). Security Groups are <strong>stateful, host-level distributed firewalls</strong> evaluated at the hypervisor level (specifically, at the virtual network interface or NIC).</p><p>The term <strong>stateful</strong> is the key to their efficiency.</p><pre>[Inbound Traffic] ──&gt; Passes Firewall Rule Check ──&gt; Establishes Active Connection State<br>                                                               │<br>                                                               ▼<br>[Outbound Response] &lt;───────────────────────────── Dynamically Allowed Back Out (No Rule Needed)</pre><p>When a packet arrives at your virtual machine, the security group evaluates its inbound rules. If a rule matches (e.g., allowing inbound HTTP traffic on TCP Port 80), the firewall permits the packet to pass and immediately creates an entry in an internal connection tracking table.</p><p>When the operating system sends a response back to the client, the firewall checks the connection tracking table. Seeing an active, established connection, <strong>it dynamically allows the outbound response packet to leave, completely bypassing all outbound rules.</strong></p><h4>AWS Security Groups vs. Azure Network Security Groups (NSGs)</h4><p>While they serve the same purpose, AWS and Azure design Security Groups with fundamentally different logic:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/940/1*Ju8zXxObDLd7PRIklYnSLg.png" /></figure><p>TABLE REF: <a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-eni.html">Elastic Network Interfaces (ENIs)</a>, <a href="https://learn.microsoft.com/en-us/azure/virtual-network/virtual-network-network-interface">Virtual Network Interface (NIC)</a></p><p>For detailed implementation steps on creating these rules, you can consult the <a href="https://docs.aws.amazon.com/vpc/latest/userguide/VPC_SecurityGroups.html">AWS Security Groups User Guide</a> or the <a href="https://learn.microsoft.com/en-us/azure/virtual-network/network-security-groups-overview">Azure NSGs Overview</a>.</p><h3>3. Stateful (Security Groups) vs. Stateless (NACLs)</h3><p>Many security architectures enforce defense-in-depth by placing another firewall layer at the subnet boundary. In AWS, this is known as a <strong>Network Access Control List (NACL)</strong>.</p><p>Unlike Security Groups, NACLs are <strong>stateless</strong>.</p><pre>Stateful (Security Groups)             Stateless (NACLs)<br> ┌───────────────────────────┐      ┌───────────────────────────┐<br> │ Evaluates INBOUND only.   │      │ Must evaluate both        │<br> │ Response is automatically │      │ INBOUND and OUTBOUND      │<br> │ permitted back out.       │      │ rules separately.         │<br> └───────────────────────────┘      └───────────────────────────┘</pre><p>Because a stateless firewall does not maintain a connection tracking table, it views every incoming and outgoing packet as an entirely independent event.</p><h4>The Ephemeral Port Trap</h4><p>This stateless nature is where many DevOps teams run into trouble. Imagine you want to allow your internal virtual machines to fetch software updates from the public internet.</p><p>In your Security Group, you simply add an <em>outbound</em> rule allowing TCP Port 443 (HTTPS) to 0.0.0.0/0. The stateful group automatically permits the incoming response.</p><p>If you try to restrict this at the NACL level, you must configure two distinct rules:</p><ol><li>An <strong>Outbound Rule</strong> allowing TCP Port 443 to the internet.</li><li>An <strong>Inbound Rule</strong> allowing traffic back in from the internet.</li></ol><p>But what port does the incoming response use? It does not connect back to your server on port 443. Instead, your server initiated the connection using a randomized client port, known as an <strong>ephemeral port</strong>.</p><p>According to the <a href="https://en.wikipedia.org/wiki/Ephemeral_port">Internet Assigned Numbers Authority (IANA)</a>, the default ephemeral port range is $49152$ to $65535$. However, different operating systems and NAT gateways utilize ranges from $1024$ to $65535$.</p><p>If your stateless NACL does not explicitly have an inbound rule allowing return traffic on these high-numbered ephemeral ports, <strong>your outbound connection will fail silently.</strong></p><p>For step-by-step guidance on setting up stateless parameters, read the <a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html">AWS NACLs User Guide</a> and check the <a href="https://docs.aws.amazon.com/vpc/latest/userguide/vpc-network-acls.html#nacl-ephemeral-ports">AWS Ephemeral Ports Guide</a>.</p><h3>Summary: Best Practices for Cloud Networking Security</h3><p>To protect your cloud architecture while maintaining seamless connectivity, keep these four guidelines in mind:</p><ol><li><strong>Calculate Subnet Growth (</strong>2^{32-n} — 5)<strong>:</strong> Account for the 5 reserved cloud IPs and build your CIDR ranges to accommodate horizontal scaling and green-blue deployment configurations.</li><li><strong>Leverage Security Group Referencing:</strong> Instead of hardcoding public or private IP addresses inside your security groups, reference the ID of other security groups. For example, configure your database’s security group to only accept inbound traffic on port 3306 if the source is your application-sg ID.</li><li><strong>Avoid SSH/RDP Exposure:</strong> Never expose administrative ports like SSH (port 22) or RDP (port 3389) directly to 0.0.0.0/0. Use modern, session-managed services like AWS Systems Manager Session Manager or Azure Bastion.</li><li><strong>Use NACLs Sparingly:</strong> Keep your NACL rules simple. Use them for broad “blocking” actions (like denying known malicious IP ranges at the subnet boundary) and rely on stateful Security Groups for granular micro-segmentation.</li></ol><p>DOWNLOAD THE RESOURCES: <a href="https://docs.google.com/document/d/1bLc2_gGPQRJdBFTGynRBDPsIT8oPhJVQiP837XQUCTA/edit?usp=sharing">https://docs.google.com/document/d/1bLc2_gGPQRJdBFTGynRBDPsIT8oPhJVQiP837XQUCTA/edit?usp=sharing</a></p><p><em>How does your team manage network boundaries? Do you use a combination of Security Groups and NACLs, or do you rely on centralized firewalls? Let’s discuss in the comments below!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=b5fdf72ca7a3" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Death to setup.py: The Modern Developer’s Guide to Python Packaging]]></title>
            <link>https://thisarad404.medium.com/death-to-setup-py-the-modern-developers-guide-to-python-packaging-c166c1acc4fd?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/c166c1acc4fd</guid>
            <category><![CDATA[python]]></category>
            <category><![CDATA[toml]]></category>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[pep-518]]></category>
            <category><![CDATA[python3]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Fri, 19 Jun 2026 05:50:30 GMT</pubDate>
            <atom:updated>2026-06-23T04:41:27.114Z</atom:updated>
            <content:encoded><![CDATA[<h3>Why PEP 517, pyproject.toml, and the “src” layout are no longer optional for production-grade Python applications.</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZmFUOM-iPkyfQhK5Lk-7GA.png" /></figure><p><em>Photo by: </em><a href="https://pin.it/53DQI86U1"><strong><em>Pinterest</em></strong></a></p><p>Imagine it’s 2015. You’ve just finished writing a revolutionary machine learning utility. You want to share it with your team, so you do what every Python developer did back then: you create a setup.py script, drop a few dependencies into a requirements.txt file, and run python setup.py install.</p><p>It works on your machine. But when your teammate pulls the code, their environment breaks. Pip complains about missing dependencies, a library compilation fails midway through, and your local development packages get inexplicably overwritten.</p><p>For decades, Python packaging was notoriously described as “the wild west.” The mechanism was brittle, imperatively designed, and plagued by security vulnerabilities.</p><p>Thankfully, those days are over.</p><p>Today, Python packaging has evolved into a robust, standardized, and fully declarative ecosystem overseen by the <a href="https://www.pypa.io/">Python Packaging Authority (PyPA)</a>. If you are still shipping code with an imperative setup.py file, it’s time to upgrade. In this guide, we’ll demystify modern Python packaging, look under the hood of <strong>PEP 517 and 518</strong>, explain why the <strong>src/ layout</strong> is your best shield against test-suite bugs, and build a production-ready package configuration from scratch.</p><h3>1. The Imperative Nightmare: Why setup.py Had to Die</h3><p>To appreciate the modern standard, we must first understand the flaws of the legacy system. Historically, creating a Python package required writing a Python script called setup.py that invoked setuptools.setup().</p><p>This meant that <strong>compiling or installing a package required running arbitrary Python code.</strong></p><p>This imperative design introduced three massive architectural flaws:</p><ol><li><strong>The Bootstrap Paradox (The Chicken-and-Egg Problem):</strong> If your setup.py script needed a helper library (like numpy or cython) to compile your code during installation, how could you run the script to install the package if those tools weren&#39;t already installed? There was no standard way to declare &quot;pre-installation&quot; dependencies.</li><li><strong>Security Risk:</strong> Because installing a package executed code locally, malicious packages could run arbitrary exploits on a developer’s machine or target CI/CD runners during the simple pip install phase.</li><li><strong>Implicit Tool Lock-in:</strong> setup.py tightly coupled the packaging ecosystem to setuptools. If a developer wanted to build packages using alternative, modern backends like Poetry, Flit, or Hatch, they were completely out of luck.</li></ol><h3>2. The Great Standardization: PEP 517, 518, and pyproject.toml</h3><p>Between 2016 and 2018, the Python community introduced two game-changing enhancements: <strong>PEP 518</strong> and <strong>PEP 517</strong>. Together, they decoupled the packaging process into two clean, separate layers: <strong>Build Frontends</strong> and <strong>Build Backends</strong>. You can read the authoritative guidelines in the official <a href="https://packaging.python.org/">Python Packaging User Guide</a>.</p><pre>┌────────────────────────────────────────────────────────┐<br>│                     BUILD FRONTEND                     │<br>│                  (e.g., pip, build)                    │<br>└──────────────────────────┬─────────────────────────────┘<br>                           │  Communicates via PEP 517<br>                           ▼<br>┌────────────────────────────────────────────────────────┐<br>│                     BUILD BACKEND                      │<br>│         (e.g., setuptools.build_meta, poetry)          │<br>└────────────────────────────────────────────────────────┘</pre><h4>PEP 518: Declaring the Tools [build-system]</h4><p><a href="https://peps.python.org/pep-0518/">PEP 518</a> established a single, unified configuration file for all Python tools: pyproject.toml. It introduced the [build-system] table to explicitly define the tooling dependencies <em>before</em> the build begins:</p><pre>[build-system]<br>requires = [&quot;setuptools&gt;=61.0&quot;, &quot;wheel&quot;]<br>build-backend = &quot;setuptools.build_meta&quot;</pre><ul><li><strong>requires</strong>: Tells the frontend (like pip) exactly what packages it needs to download into an isolated, temporary virtual environment to compile your package.</li><li><strong>build-backend</strong>: Explicitly points to the engine responsible for generating your distribution files.</li></ul><h4>PEP 517: Decoupling the Engine</h4><p><a href="https://peps.python.org/pep-0517/">PEP 517</a> defines a standard API that build backends must implement. This means a frontend tool like pip doesn’t care whether you use setuptools, poetry, hatch, or flit. As long as the backend implements the standard hooks (like build_wheel and build_sdist), your project compiles flawlessly.</p><h3>3. The src/ Layout Shield: Preventing Silent Test Failures</h3><p>If you look at modern enterprise Python codebases, you will notice that the core package directory isn’t located in the repository’s root. Instead, it is nested inside a /src directory:</p><pre>my_project/<br>├── pyproject.toml<br>├── src/<br>│   └── fraud_detection/      &lt;-- Your actual package code lives here<br>│       ├── __init__.py<br>│       └── predict.py<br>└── tests/<br>    └── test_predict.py</pre><p>This is called the <strong>src/ layout</strong>, and it is highly recommended by PyPA over the traditional <strong>flat layout</strong> (where fraud_detection/ sits directly in the root directory alongside pyproject.toml).</p><h3>Why is the Flat Layout a Trap?</h3><p>When you run a testing framework like pytest in a flat-layout repository, the current working directory is added to Python&#39;s import path (sys.path).</p><p>If your test suite imports your library with import fraud_detection, Python will import the <strong>raw source files</strong> directly from your local directory, <strong>not</strong> the compiled package you built.</p><p>This introduces two severe vulnerabilities:</p><ol><li><strong>False Positives:</strong> Your tests might pass perfectly on your machine, but fail in production because a crucial asset file or compiled extension was left out of the built package (.whl).</li><li><strong>Missing Dependency Detection:</strong> Since your local files are imported directly, your tests won’t catch whether you forgot to list a dependency in pyproject.toml.</li></ol><p>By nesting your code under src/, Python <em>cannot</em> find your package unless it has been explicitly installed into your active virtual environment. Your test runner is forced to test the <strong>actual built artifact</strong>, ensuring what you ship matches what you tested. For a deeper discussion on this concept, refer to the <a href="https://docs.pytest.org/en/stable/explanation/goodpractices.html#src-layout">Pytest Documentation on Import Mechanics</a>.</p><h3>4. Building a Production-Ready pyproject.toml Blueprint</h3><p>Let’s put this theory into practice. Below is a highly robust, production-grade template for an enterprise machine learning package (e.g., fraud_detection). This configuration follows the modern <a href="https://packaging.python.org/en/latest/specifications/pyproject-toml/">pyproject.toml specification</a> and integrates metadata, strict Python restrictions, dependency specifications, and URL references.</p><pre>[build-system]<br>requires = [&quot;setuptools&gt;=61.0&quot;, &quot;wheel&quot;]<br>build-backend = &quot;setuptools.build_meta&quot;<br><br>[project]<br>name = &quot;fraud_detection&quot;<br>version = &quot;0.1.0&quot;<br>description = &quot;Enterprise-grade real-time fraud prediction engine.&quot;<br>readme = &quot;README.md&quot;<br>requires-python = &quot;&gt;=3.10&quot;<br>license = { text = &quot;MIT&quot; }<br>authors = [<br>    { name = &quot;xFusionCorp DevOps&quot;, email = &quot;devops@xfusioncorp.com&quot; }<br>]<br>classifiers = [<br>    &quot;Development Status :: 4 - Beta&quot;,<br>    &quot;Intended Audience :: Developers&quot;,<br>    &quot;Intended Audience :: Science/Research&quot;,<br>    &quot;License :: OSI Approved :: MIT License&quot;,<br>    &quot;Operating System :: OS Independent&quot;,<br>    &quot;Programming Language :: Python :: 3.10&quot;,<br>    &quot;Programming Language :: Python :: 3.11&quot;,<br>    &quot;Programming Language :: Python :: 3.12&quot;,<br>    &quot;Topic :: Scientific/Engineering :: Artificial Intelligence&quot;<br>]<br>dependencies = [<br>    &quot;scikit-learn&gt;=1.2.0&quot;,<br>    &quot;pandas&gt;=2.0.0&quot;,<br>    &quot;numpy&gt;=1.24.0&quot;<br>]<br><br>[project.urls]<br>&quot;Homepage&quot; = &quot;[https://github.com/xfusioncorp/fraud-detection](https://github.com/xfusioncorp/fraud-detection)&quot;<br>&quot;Bug Tracker&quot; = &quot;[https://github.com/xfusioncorp/fraud-detection/issues](https://github.com/xfusioncorp/fraud-detection/issues)&quot;<br>&quot;Documentation&quot; = &quot;[https://xfusioncorp.github.io/fraud-detection/](https://xfusioncorp.github.io/fraud-detection/)&quot;</pre><h3>Breaking Down the Configuration:</h3><ul><li><strong>requires-python</strong>: Strictly guards your runtime environment. Trying to install this package on Python 3.9 or older will fail gracefully, avoiding obscure runtime syntax errors.</li><li><strong>classifiers</strong>: Standardized tags used by PyPI (Python Package Index) to help developers search for stable, compatible, and open-source software. You can browse the <a href="https://pypi.org/classifiers/">complete list of Trove Classifiers on PyPI</a>.</li><li><strong>dependencies</strong>: Declares semantic version pins (&gt;=) ensuring your package installs compatible ecosystem versions while preventing breaks from outdated APIs. See the <a href="https://packaging.python.org/en/latest/specifications/dependency-specifiers/">specifications for dependency specifiers</a> for more formatting patterns.</li></ul><p>For an exhaustive guide on configuration fields, see the <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">PyPA Guide on Writing your pyproject.toml</a>.</p><h3>5. From Code to Artifact: Compiling the Wheel</h3><p>Once your configuration is written, building your package is incredibly simple. Install the official PyPA build frontend and compile the source according to the <a href="https://packaging.python.org/en/latest/tutorials/packaging-projects/">PyPA packaging tutorial</a>:</p><pre># Install the build frontend<br>python3 -m pip install --upgrade build<br><br># Compile your package from the project root<br>python3 -m build</pre><p>This commands reads your pyproject.toml file, provisions an isolated build environment, processes your files under src/, and outputs your artifacts into a new /dist folder.</p><p>Inside dist/, you will find two files:</p><ol><li><strong>fraud_detection-0.1.0.tar.gz (Source Distribution):</strong> A compressed archive of your raw source code, ideal for fallback compilations on highly custom architectures.</li><li><strong>fraud_detection-0.1.0-py3-none-any.whl (The Wheel):</strong> A pre-built binary distribution. Installing this is lightning fast. Pip simply unzips it directly into the system&#39;s runtime folder—no compilers, no setup script runs, and no overhead. Refer to <a href="https://peps.python.org/pep-0427/">PEP 427</a> for the complete specification of the wheel binary format.</li></ol><h3>How to Inspect Your Wheel</h3><p>To prove that a wheel is just a glorified ZIP file, try peaking inside it using standard command-line tools:</p><pre>unzip -l dist/fraud_detection-0.1.0-py3-none-any.whl</pre><p>You’ll see a clean, predictable file output showing your internal library structure alongside a .dist-info directory containing your parsed metadata and install records.</p><h3>Summary Cheat Sheet: Modern Python Packaging</h3><p>To ensure your code meets modern software engineering standards, verify your project structures align with this quick checklist:</p><ul><li>[ ] <strong>No </strong><strong>setup.py:</strong> Use pyproject.toml for all package metadata and dependency definitions.</li><li>[ ] <strong>Explicit Build System:</strong> Ensure your [build-system] table is configured with a valid PEP 517 backend.</li><li>[ ] <strong>Use the </strong><strong>src/ Layout:</strong> Protect your codebase against silent testing errors by nesting code inside src/.</li><li>[ ] <strong>Define Python Guards:</strong> Explicitly declare requires-python limits to prevent user deployment failures on unsupported systems.</li><li>[ ] <strong>Clean Compilation:</strong> Build artifacts using python -m build and distribute the compiled .whl files.</li></ul><p>By upgrading your packaging architecture to modern standards, you eliminate local machine inconsistencies, secure your distribution pipeline, and future-proof your application for professional, cloud-native deployments.</p><p>Documentations to refer more:</p><ul><li><strong>PyPA Authority Link:</strong> Added a link to the <a href="https://www.pypa.io/">Python Packaging Authority (PyPA)</a> in the introduction.</li><li><strong>Standardization &amp; Spec Overview:</strong> Linked the main <a href="https://packaging.python.org/en/latest/">Python Packaging User Guide</a> under the PEP 517/518 flow diagram.</li><li><strong>PEP References:</strong> Added direct links to <a href="https://peps.python.org/pep-0517/">PEP 517</a> and <a href="https://peps.python.org/pep-0518/">PEP 518</a>.</li><li><strong>Source Layout Import Mechanics:</strong> Added an import mechanics link to the official <a href="https://docs.pytest.org/en/stable/explanation/goodpractices.html#src-layout">Pytest Documentation</a>.</li><li><strong>Specification Details:</strong> Added links to the <a href="https://packaging.python.org/en/latest/specifications/pyproject-toml/">pyproject.toml specification</a>, PyPI’s <a href="https://pypi.org/classifiers/">Trove Classifiers</a>, and <a href="https://packaging.python.org/en/latest/specifications/dependency-specifiers/">Dependency Specifiers</a>.</li><li><strong>Detailed Setup Guide:</strong> Linked the official <a href="https://packaging.python.org/en/latest/guides/writing-pyproject-toml/">Writing your pyproject.toml Guide</a> and PyPA’s <a href="https://packaging.python.org/en/latest/tutorials/packaging-projects/">Packaging Projects Tutorial</a>.</li><li><strong>Wheel Spec (PEP 427):</strong> Integrated a link to the binary distribution specification <a href="https://peps.python.org/pep-0427/">PEP 427</a>.</li></ul><p><em>Did you find this guide helpful? Have you successfully migrated your legacy setups to </em><em>pyproject.toml? Let me know in the comments below, and don&#39;t forget to share this article with your DevOps and Python engineering teams!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c166c1acc4fd" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Debugging the “Restarting” Loop: A Guide to Troubleshooting Docker Deployments on a VPS]]></title>
            <link>https://thisarad404.medium.com/debugging-the-restarting-loop-a-guide-to-troubleshooting-docker-deployments-on-a-vps-a0f0d08c1015?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/a0f0d08c1015</guid>
            <category><![CDATA[troubleshooting]]></category>
            <category><![CDATA[deployment]]></category>
            <category><![CDATA[troubleshooting-tips]]></category>
            <category><![CDATA[debugging]]></category>
            <category><![CDATA[ci-cd-pipeline]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Mon, 26 Jan 2026 16:57:01 GMT</pubDate>
            <atom:updated>2026-01-26T16:57:01.202Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ZhnYe_0TWmCHfR9Pfi_IeA.png" /></figure><p>How I fixed a crashing backend container when my CI/CD pipeline said everything was fine.</p><p>We’ve all been there. You push your code, watch the GitHub Actions workflow turn green, and feel that sense of satisfaction. You go to check your live site… and it’s down.</p><p>This happened to me today while deploying my Mobile Distribution System backend. Here is a breakdown of what went wrong, how I found it, and how I fixed it.</p><h3>The Problem</h3><p>After a fresh deployment, my API was unreachable. I SSH’d into my Ubuntu VPS to check the status of my containers.</p><pre>docker ps</pre><p>The output was not what I wanted to see: CONTAINER ID ... STATUS: Restarting (1) 14 seconds ago ... mobile-dist-backend</p><p>The container was stuck in a boot loop. It would start, crash, and try again.</p><h3>The Investigation</h3><p>When a Docker container crashes immediately, it usually means the application inside can’t start. In Node.js apps, this is often due to missing environment variables or database connection errors.</p><p>I checked my file system and realized I had made a mess. I had two versions of my project folder:</p><ol><li>One in my home directory (~/mds-server)</li><li>One in the deployment directory (/opt/mobile-dist-app)</li></ol><p>My Docker container was configured to look for an .env file in /opt/, but I had been editing the .env file in ~/. As a result, the container was launching with an empty or incorrect configuration. It had no MONGODB_URI, so the Mongoose connection failed, and the app crashed.</p><h3>The Solution</h3><p><strong>1. Clean House</strong> First, I removed the incorrect directory to prevent future confusion.</p><pre>rm -rf ~/mds-server</pre><p><strong>2. Update the Source of Truth</strong> I navigated to the correct directory and updated the .env file with the correct MongoDB credentials.</p><pre>cd /opt/mobile-dist-app<br>nano .env</pre><p><strong>3. Hard Reset the Container</strong> I manually stopped and removed the glitchy container, then ran the run command manually to ensure it picked up the new environment file.</p><pre>docker stop mobile-dist-backend<br>docker rm mobile-dist-backend</pre><pre>docker run -d \<br>  --name mobile-dist-backend \<br>  --restart unless-stopped \<br>  -p 5000:5000 \<br>  --env-file /opt/mobile-dist-app/.env \<br>  ghcr.io/innovior-developers/mds-server:latest</pre><h3>The Result</h3><p>A quick check with docker ps confirmed the fix: Up 33 seconds (healthy)</p><h3>Conclusion</h3><p>Automation is great, but it relies on your server environment being set up correctly. If your Docker container keeps restarting, 9 times out of 10, it’s a configuration file in the wrong place.</p><p>Happy coding!</p><p>#Docker #DevOps #Programming #Tutorial</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a0f0d08c1015" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Flutter BLoC Architecture: A Complete Guide to Building Scalable Apps]]></title>
            <link>https://thisarad404.medium.com/flutter-bloc-architecture-a-complete-guide-to-building-scalable-apps-c2e8e50c5447?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/c2e8e50c5447</guid>
            <category><![CDATA[flutter-app-development]]></category>
            <category><![CDATA[flutter-widget]]></category>
            <category><![CDATA[flutter-ui]]></category>
            <category><![CDATA[flutter]]></category>
            <category><![CDATA[bloc-architecture]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sat, 24 Jan 2026 18:32:30 GMT</pubDate>
            <atom:updated>2026-01-24T18:32:30.499Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3pphHYOMrFzeTe_J44Ly0g.png" /></figure><p>Are you facing these challenges in your Flutter development journey?</p><ul><li>Your code is becoming increasingly messy and hard to maintain</li><li>Business logic is tightly coupled with UI code</li><li>Testing feels like an uphill battle</li><li>Scaling your application seems impossible</li><li>State management is giving you headaches</li></ul><p>If you answered yes to any of these, <strong>BLoC (Business Logic Component) Architecture</strong> is the solution you’ve been looking for. In this comprehensive guide, I’ll walk you through everything you need to know about BLoC architecture with practical examples and real-world applications.</p><h3>What is BLoC Architecture?</h3><p>BLoC is an architectural pattern introduced by Google that emphasizes the separation of business logic from the UI layer using streams and reactive programming.</p><h3>Understanding BLoC with a Simple Analogy</h3><p>Think of a restaurant:</p><ol><li><strong>Customer (UI)</strong> — You (the user) place an order with the waiter</li><li><strong>Waiter (BLoC)</strong> — Takes your order and passes it to the kitchen</li><li><strong>Kitchen (Data Layer)</strong> — Prepares your food</li><li><strong>Waiter (BLoC)</strong> — Delivers the finished meal back to you</li><li><strong>Customer (UI)</strong> — Enjoys the meal</li></ol><p>This is exactly how BLoC works:</p><ul><li><strong>UI</strong> is the customer — handles user interactions</li><li><strong>BLoC</strong> is the waiter — receives events and emits states</li><li><strong>Repository/Data Layer</strong> is the kitchen — fetches and processes data</li></ul><h3>Core Components of BLoC Architecture</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zRdU8dtkanpgDzCV8BtvHw.png" /></figure><h3>1. Events (Requests)</h3><p>Events represent user interactions or system triggers. They’re the “commands” sent to the BLoC.</p><pre>// User presses a button<br>// Requests data to be loaded<br>// Submits a form<br><br>abstract class AuthEvent {}<br>class LoginButtonPressed extends AuthEvent {<br>  final String email;<br>  final String password;<br>  <br>  LoginButtonPressed({required this.email, required this.password});<br>}<br>class LogoutButtonPressed extends AuthEvent {}<br>class CheckAuthStatus extends AuthEvent {}</pre><p>Events are simple Dart classes that carry the necessary data for the BLoC to process the request.</p><h3>2. States (Representations of UI)</h3><p>States represent the current condition of your UI at any given moment.</p><pre>abstract class AuthState {}<br><br>class AuthInitial extends AuthState {}<br>class AuthLoading extends AuthState {}<br>class AuthAuthenticated extends AuthState {<br>  final User user;<br>  <br>  AuthAuthenticated({required this.user});<br>}<br>class AuthUnauthenticated extends AuthState {}<br>class AuthError extends AuthState {<br>  final String message;<br>  <br>  AuthError({required this.message});<br>}</pre><p>Each state tells the UI exactly how it should render itself. Loading state shows a spinner, error state displays an error message, and so on.</p><h3>3. BLoC (The Brain)</h3><p>The BLoC is the component that receives events, processes business logic, and emits states.</p><pre>class AuthBloc extends Bloc&lt;AuthEvent, AuthState&gt; {<br>  final AuthRepository authRepository;<br>  <br>  AuthBloc({required this.authRepository}) : super(AuthInitial()) {<br>    <br>    // Handle login event<br>    on&lt;LoginButtonPressed&gt;((event, emit) async {<br>      // Emit loading state<br>      emit(AuthLoading());<br>      <br>      try {<br>        // Fetch data from repository<br>        final user = await authRepository.login(<br>          email: event.email,<br>          password: event.password,<br>        );<br>        <br>        // Emit authenticated state on success<br>        emit(AuthAuthenticated(user: user));<br>      } catch (e) {<br>        // Emit error state on failure<br>        emit(AuthError(message: e.toString()));<br>      }<br>    });<br>    <br>    // Handle logout event<br>    on&lt;LogoutButtonPressed&gt;((event, emit) async {<br>      await authRepository.logout();<br>      emit(AuthUnauthenticated());<br>    });<br>  }<br>}</pre><p>The BLoC acts as the “brain” of your feature, making decisions about what to do with incoming events and determining what state to emit.</p><h3>Integrating BLoC with Flutter UI</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fl4HX7c33ARdfhUMGn1fLA.png" /></figure><h3>BlocProvider — Adding BLoC to the Widget Tree</h3><pre>class MyApp extends StatelessWidget {<br>  @override<br>  Widget build(BuildContext context) {<br>    return BlocProvider(<br>      // Create BLoC instance<br>      create: (context) =&gt; AuthBloc(<br>        authRepository: AuthRepository(),<br>      ),<br>      child: MaterialApp(<br>        home: LoginPage(),<br>      ),<br>    );<br>  }<br>}</pre><h3>BlocBuilder — Updating UI Based on State Changes</h3><pre>class LoginPage extends StatelessWidget {<br>  @override<br>  Widget build(BuildContext context) {<br>    return Scaffold(<br>      appBar: AppBar(title: Text(&#39;Login&#39;)),<br>      body: BlocBuilder&lt;AuthBloc, AuthState&gt;(<br>        builder: (context, state) {<br>          // Show loading indicator during authentication<br>          if (state is AuthLoading) {<br>            return Center(child: CircularProgressIndicator());<br>          }<br>          <br>          // Show error message<br>          if (state is AuthError) {<br>            return Center(<br>              child: Column(<br>                mainAxisAlignment: MainAxisAlignment.center,<br>                children: [<br>                  Text(&#39;Error: ${state.message}&#39;),<br>                  ElevatedButton(<br>                    onPressed: () {<br>                      // Retry logic<br>                    },<br>                    child: Text(&#39;Retry&#39;),<br>                  ),<br>                ],<br>              ),<br>            );<br>          }<br>          <br>          // Navigate to home on successful authentication<br>          if (state is AuthAuthenticated) {<br>            return HomePage(user: state.user);<br>          }<br>          <br>          // Default: show login form<br>          return LoginForm();<br>        },<br>      ),<br>    );<br>  }<br>}</pre><h3>BlocListener — Handling Side Effects</h3><pre>class LoginForm extends StatelessWidget {<br>  final emailController = TextEditingController();<br>  final passwordController = TextEditingController();<br>  <br>  @override<br>  Widget build(BuildContext context) {<br>    return BlocListener&lt;AuthBloc, AuthState&gt;(<br>      listener: (context, state) {<br>        // Navigate on success<br>        if (state is AuthAuthenticated) {<br>          Navigator.pushReplacement(<br>            context,<br>            MaterialPageRoute(builder: (_) =&gt; HomePage()),<br>          );<br>        }<br>        <br>        // Show snackbar on error<br>        if (state is AuthError) {<br>          ScaffoldMessenger.of(context).showSnackBar(<br>            SnackBar(content: Text(state.message)),<br>          );<br>        }<br>      },<br>      child: Padding(<br>        padding: EdgeInsets.all(16),<br>        child: Column(<br>          mainAxisAlignment: MainAxisAlignment.center,<br>          children: [<br>            TextField(<br>              controller: emailController,<br>              decoration: InputDecoration(labelText: &#39;Email&#39;),<br>            ),<br>            SizedBox(height: 16),<br>            TextField(<br>              controller: passwordController,<br>              decoration: InputDecoration(labelText: &#39;Password&#39;),<br>              obscureText: true,<br>            ),<br>            SizedBox(height: 24),<br>            ElevatedButton(<br>              onPressed: () {<br>                // Dispatch event to BLoC<br>                context.read&lt;AuthBloc&gt;().add(<br>                  LoginButtonPressed(<br>                    email: emailController.text,<br>                    password: passwordController.text,<br>                  ),<br>                );<br>              },<br>              child: Text(&#39;Login&#39;),<br>            ),<br>          ],<br>        ),<br>      ),<br>    );<br>  }<br>}</pre><p><strong>Key Differences:</strong></p><ul><li><strong>BlocBuilder</strong> rebuilds the UI when state changes</li><li><strong>BlocListener</strong> executes one-time actions (navigation, dialogs, snackbars)</li><li><strong>BlocConsumer</strong> combines both functionalities</li></ul><h3>Real-World Example: Building a Todo App with BLoC</h3><p>Let’s build a complete todo application using BLoC architecture to see how everything comes together.</p><h3>Step 1: Define the Model</h3><pre>class Todo {<br>  final String id;<br>  final String title;<br>  final bool isCompleted;<br>  <br>  Todo({<br>    required this.id,<br>    required this.title,<br>    this.isCompleted = false,<br>  });<br>  <br>  Todo copyWith({String? title, bool? isCompleted}) {<br>    return Todo(<br>      id: id,<br>      title: title ?? this.title,<br>      isCompleted: isCompleted ?? this.isCompleted,<br>    );<br>  }<br>}</pre><h3>Step 2: Define Events</h3><pre>abstract class TodoEvent {}<br><br>class LoadTodos extends TodoEvent {}<br>class AddTodo extends TodoEvent {<br>  final String title;<br>  AddTodo(this.title);<br>}<br>class UpdateTodo extends TodoEvent {<br>  final Todo todo;<br>  UpdateTodo(this.todo);<br>}<br>class DeleteTodo extends TodoEvent {<br>  final String id;<br>  DeleteTodo(this.id);<br>}<br>class ToggleTodoComplete extends TodoEvent {<br>  final String id;<br>  ToggleTodoComplete(this.id);<br>}</pre><h3>Step 3: Define States</h3><pre>abstract class TodoState {}<br><br>class TodoInitial extends TodoState {}<br>class TodoLoading extends TodoState {}<br>class TodoLoaded extends TodoState {<br>  final List&lt;Todo&gt; todos;<br>  <br>  TodoLoaded(this.todos);<br>}<br>class TodoError extends TodoState {<br>  final String message;<br>  <br>  TodoError(this.message);<br>}</pre><h3>Step 4: Create the Repository</h3><pre>class TodoRepository {<br>  // Simulate API calls or database operations<br>  <br>  Future&lt;List&lt;Todo&gt;&gt; getTodos() async {<br>    // Simulate network delay<br>    await Future.delayed(Duration(seconds: 1));<br>    return [<br>      Todo(id: &#39;1&#39;, title: &#39;Learn Flutter&#39;),<br>      Todo(id: &#39;2&#39;, title: &#39;Master BLoC pattern&#39;),<br>    ];<br>  }<br>  <br>  Future&lt;void&gt; addTodo(String title) async {<br>    await Future.delayed(Duration(milliseconds: 500));<br>    // API call to add todo<br>  }<br>  <br>  Future&lt;void&gt; updateTodo(Todo todo) async {<br>    await Future.delayed(Duration(milliseconds: 500));<br>    // API call to update todo<br>  }<br>  <br>  Future&lt;void&gt; deleteTodo(String id) async {<br>    await Future.delayed(Duration(milliseconds: 500));<br>    // API call to delete todo<br>  }<br>}</pre><h3>Step 5: Implement the BLoC</h3><pre>class TodoBloc extends Bloc&lt;TodoEvent, TodoState&gt; {<br>  final TodoRepository repository;<br>  List&lt;Todo&gt; _todos = [];<br>  <br>  TodoBloc({required this.repository}) : super(TodoInitial()) {<br>    <br>    // Load Todos<br>    on&lt;LoadTodos&gt;((event, emit) async {<br>      emit(TodoLoading());<br>      try {<br>        _todos = await repository.getTodos();<br>        emit(TodoLoaded(List.from(_todos)));<br>      } catch (e) {<br>        emit(TodoError(e.toString()));<br>      }<br>    });<br>    <br>    // Add Todo<br>    on&lt;AddTodo&gt;((event, emit) async {<br>      try {<br>        await repository.addTodo(event.title);<br>        _todos.add(Todo(<br>          id: DateTime.now().toString(),<br>          title: event.title,<br>        ));<br>        emit(TodoLoaded(List.from(_todos)));<br>      } catch (e) {<br>        emit(TodoError(e.toString()));<br>      }<br>    });<br>    <br>    // Toggle Complete<br>    on&lt;ToggleTodoComplete&gt;((event, emit) async {<br>      try {<br>        final index = _todos.indexWhere((todo) =&gt; todo.id == event.id);<br>        if (index != -1) {<br>          _todos[index] = _todos[index].copyWith(<br>            isCompleted: !_todos[index].isCompleted,<br>          );<br>          await repository.updateTodo(_todos[index]);<br>          emit(TodoLoaded(List.from(_todos)));<br>        }<br>      } catch (e) {<br>        emit(TodoError(e.toString()));<br>      }<br>    });<br>    <br>    // Delete Todo<br>    on&lt;DeleteTodo&gt;((event, emit) async {<br>      try {<br>        await repository.deleteTodo(event.id);<br>        _todos.removeWhere((todo) =&gt; todo.id == event.id);<br>        emit(TodoLoaded(List.from(_todos)));<br>      } catch (e) {<br>        emit(TodoError(e.toString()));<br>      }<br>    });<br>  }<br>}</pre><h3>Step 6: Build the UI</h3><pre>class TodoPage extends StatelessWidget {<br>  @override<br>  Widget build(BuildContext context) {<br>    return BlocProvider(<br>      create: (context) =&gt; TodoBloc(<br>        repository: TodoRepository(),<br>      )..add(LoadTodos()), // Load todos when page opens<br>      child: Scaffold(<br>        appBar: AppBar(title: Text(&#39;Todo App with BLoC&#39;)),<br>        body: BlocBuilder&lt;TodoBloc, TodoState&gt;(<br>          builder: (context, state) {<br>            if (state is TodoLoading) {<br>              return Center(child: CircularProgressIndicator());<br>            }<br>            <br>            if (state is TodoError) {<br>              return Center(<br>                child: Column(<br>                  mainAxisAlignment: MainAxisAlignment.center,<br>                  children: [<br>                    Text(&#39;Error: ${state.message}&#39;),<br>                    ElevatedButton(<br>                      onPressed: () {<br>                        context.read&lt;TodoBloc&gt;().add(LoadTodos());<br>                      },<br>                      child: Text(&#39;Retry&#39;),<br>                    ),<br>                  ],<br>                ),<br>              );<br>            }<br>            <br>            if (state is TodoLoaded) {<br>              if (state.todos.isEmpty) {<br>                return Center(<br>                  child: Text(&#39;No todos yet. Add one!&#39;),<br>                );<br>              }<br>              <br>              return ListView.builder(<br>                itemCount: state.todos.length,<br>                itemBuilder: (context, index) {<br>                  final todo = state.todos[index];<br>                  return ListTile(<br>                    leading: Checkbox(<br>                      value: todo.isCompleted,<br>                      onChanged: (_) {<br>                        context.read&lt;TodoBloc&gt;().add(<br>                          ToggleTodoComplete(todo.id),<br>                        );<br>                      },<br>                    ),<br>                    title: Text(<br>                      todo.title,<br>                      style: TextStyle(<br>                        decoration: todo.isCompleted<br>                            ? TextDecoration.lineThrough<br>                            : null,<br>                      ),<br>                    ),<br>                    trailing: IconButton(<br>                      icon: Icon(Icons.delete),<br>                      onPressed: () {<br>                        context.read&lt;TodoBloc&gt;().add(<br>                          DeleteTodo(todo.id),<br>                        );<br>                      },<br>                    ),<br>                  );<br>                },<br>              );<br>            }<br>            <br>            return Center(child: Text(&#39;Something went wrong&#39;));<br>          },<br>        ),<br>        floatingActionButton: FloatingActionButton(<br>          onPressed: () =&gt; _showAddTodoDialog(context),<br>          child: Icon(Icons.add),<br>        ),<br>      ),<br>    );<br>  }<br>  <br>  void _showAddTodoDialog(BuildContext context) {<br>    final controller = TextEditingController();<br>    <br>    showDialog(<br>      context: context,<br>      builder: (dialogContext) =&gt; AlertDialog(<br>        title: Text(&#39;New Todo&#39;),<br>        content: TextField(<br>          controller: controller,<br>          decoration: InputDecoration(hintText: &#39;Enter todo title&#39;),<br>        ),<br>        actions: [<br>          TextButton(<br>            onPressed: () =&gt; Navigator.pop(dialogContext),<br>            child: Text(&#39;Cancel&#39;),<br>          ),<br>          TextButton(<br>            onPressed: () {<br>              if (controller.text.isNotEmpty) {<br>                context.read&lt;TodoBloc&gt;().add(AddTodo(controller.text));<br>                Navigator.pop(dialogContext);<br>              }<br>            },<br>            child: Text(&#39;Add&#39;),<br>          ),<br>        ],<br>      ),<br>    );<br>  }<br>}</pre><h3>Advantages of BLoC Architecture</h3><h3>1. Separation of Concerns</h3><p>Business logic is completely isolated from UI code, making both easier to maintain and modify independently.</p><h3>2. Exceptional Testability</h3><p>BLoCs are plain Dart classes that can be unit tested without any widget testing infrastructure.</p><pre>void main() {<br>  group(&#39;TodoBloc&#39;, () {<br>    late TodoBloc todoBloc;<br>    late TodoRepository repository;<br>    <br>    setUp(() {<br>      repository = MockTodoRepository();<br>      todoBloc = TodoBloc(repository: repository);<br>    });<br>    <br>    test(&#39;Initial state should be TodoInitial&#39;, () {<br>      expect(todoBloc.state, isA&lt;TodoInitial&gt;());<br>    });<br>    <br>    test(&#39;LoadTodos should emit TodoLoading then TodoLoaded&#39;, () async {<br>      // Arrange<br>      final todos = [Todo(id: &#39;1&#39;, title: &#39;Test&#39;)];<br>      when(() =&gt; repository.getTodos()).thenAnswer((_) async =&gt; todos);<br>      <br>      // Assert<br>      expectLater(<br>        todoBloc.stream,<br>        emitsInOrder([<br>          isA&lt;TodoLoading&gt;(),<br>          isA&lt;TodoLoaded&gt;(),<br>        ]),<br>      );<br>      <br>      // Act<br>      todoBloc.add(LoadTodos());<br>    });<br>  });<br>}</pre><h3>3. Reusability</h3><p>A single BLoC can be shared across multiple widgets and even different platforms (web, mobile, desktop).</p><h3>4. Scalability</h3><p>As your app grows, BLoC architecture scales gracefully. Adding new features doesn’t impact existing code.</p><h3>5. Predictable State Management</h3><p>Unidirectional data flow makes it easy to understand how data moves through your app and simplifies debugging.</p><h3>6. Time Travel Debugging</h3><p>You can track all state changes, making it easy to trace how you arrived at the current state.</p><h3>Disadvantages to Consider</h3><h3>1. Boilerplate Code</h3><p>You need to create multiple files for events, states, and BLoC classes. This can feel like overkill for simple apps.</p><h3>2. Learning Curve</h3><p>Beginners may find BLoC concepts challenging initially. Understanding streams and reactive programming is essential.</p><h3>3. Development Time</h3><p>Initial setup takes longer compared to simpler state management solutions like Provider.</p><h3>BLoC vs Other State Management Solutions</h3><h3>BLoC vs Provider</h3><p><strong>Provider:</strong></p><ul><li>Simpler and more straightforward</li><li>Less boilerplate code</li><li>Good for small to medium apps</li></ul><p><strong>BLoC:</strong></p><ul><li>More structured and scalable</li><li>Better for large applications</li><li>Easier to test</li><li>Better for team collaboration</li></ul><h3>BLoC vs Riverpod</h3><p><strong>Riverpod:</strong></p><ul><li>Modern and flexible</li><li>Compile-time safety</li><li>Less boilerplate than BLoC</li></ul><p><strong>BLoC:</strong></p><ul><li>Well-established and mature</li><li>Larger community</li><li>Better documentation</li><li>More opinionated structure</li></ul><h3>BLoC vs GetX</h3><p><strong>GetX:</strong></p><ul><li>All-in-one solution (routing, state management, dependency injection)</li><li>Less code required</li><li>Faster development</li></ul><p><strong>BLoC:</strong></p><ul><li>Follows the single responsibility principle</li><li>Better separation of concerns</li><li>More testable</li><li>Industry standard</li></ul><h3>Best Practices</h3><h3>1. Use Equatable for State Comparison</h3><pre>import &#39;package:equatable/equatable.dart&#39;;<br><br>class TodoLoaded extends Equatable {<br>  final List&lt;Todo&gt; todos;<br>  <br>  const TodoLoaded(this.todos);<br>  <br>  @override<br>  List&lt;Object&gt; get props =&gt; [todos];<br>}</pre><p>Equatable helps BLoC accurately compare states to determine if a rebuild is necessary.</p><h3>2. One BLoC Per Feature</h3><p>Create one BLoC for each feature:</p><ul><li>AuthBloc for authentication</li><li>ProfileBloc for user profile</li><li>TodoBloc for todo management</li></ul><h3>3. Use the Repository Pattern</h3><p>Abstract your data sources behind repositories. BLoCs shouldn’t know where data comes from.</p><pre>class TodoRepository {<br>  final TodoApiClient apiClient;<br>  final TodoLocalDatabase database;<br>  <br>  TodoRepository({<br>    required this.apiClient,<br>    required this.database,<br>  });<br>  <br>  Future&lt;List&lt;Todo&gt;&gt; getTodos() async {<br>    try {<br>      // Try to get from API<br>      return await apiClient.fetchTodos();<br>    } catch (e) {<br>      // Fallback to local database<br>      return await database.getTodos();<br>    }<br>  }<br>}</pre><h3>4. Handle Errors Properly</h3><p>Always use try-catch blocks in event handlers.</p><pre>on&lt;LoadTodos&gt;((event, emit) async {<br>  emit(TodoLoading());<br>  try {<br>    final todos = await repository.getTodos();<br>    emit(TodoLoaded(todos));<br>  } on NetworkException catch (e) {<br>    emit(TodoError(&#39;Network error: ${e.message}&#39;));<br>  } on CacheException catch (e) {<br>    emit(TodoError(&#39;Cache error: ${e.message}&#39;));<br>  } catch (e) {<br>    emit(TodoError(&#39;Unknown error: ${e.toString()}&#39;));<br>  }<br>});</pre><h3>5. Use BlocObserver for Debugging</h3><pre>class SimpleBlocObserver extends BlocObserver {<br>  @override<br>  void onEvent(Bloc bloc, Object? event) {<br>    super.onEvent(bloc, event);<br>    print(&#39;Event: $event&#39;);<br>  }<br>  <br>  @override<br>  void onTransition(Bloc bloc, Transition transition) {<br>    super.onTransition(bloc, transition);<br>    print(&#39;Transition: $transition&#39;);<br>  }<br>  <br>  @override<br>  void onError(BlocBase bloc, Object error, StackTrace stackTrace) {<br>    print(&#39;Error: $error&#39;);<br>    super.onError(bloc, error, stackTrace);<br>  }<br>}<br><br>void main() {<br>  Bloc.observer = SimpleBlocObserver();<br>  runApp(MyApp());<br>}</pre><h3>6. Keep States Immutable</h3><p>States should always be immutable. Create new instances instead of modifying existing ones.</p><pre>// ❌ Wrong<br>class TodoState {<br>  List&lt;Todo&gt; todos;<br>  TodoState(this.todos);<br>}<br><br>// ✅ Correct<br>class TodoState {<br>  final List&lt;Todo&gt; todos;<br>  const TodoState(this.todos);<br>}</pre><h3>7. Close BLoCs Properly</h3><p>Always close BLoCs when done to prevent memory leaks.</p><pre>@override<br>void dispose() {<br>  _todoBloc.close();<br>  super.dispose();<br>}</pre><h3>Recommended Project Structure</h3><pre>lib/<br>├── data/<br>│   ├── models/<br>│   │   └── todo.dart<br>│   ├── repositories/<br>│   │   └── todo_repository.dart<br>│   └── data_sources/<br>│       ├── todo_api_client.dart<br>│       └── todo_local_database.dart<br>├── logic/<br>│   └── bloc/<br>│       ├── auth/<br>│       │   ├── auth_bloc.dart<br>│       │   ├── auth_event.dart<br>│       │   └── auth_state.dart<br>│       └── todo/<br>│           ├── todo_bloc.dart<br>│           ├── todo_event.dart<br>│           └── todo_state.dart<br>├── presentation/<br>│   ├── pages/<br>│   │   ├── home_page.dart<br>│   │   ├── login_page.dart<br>│   │   └── todo_page.dart<br>│   └── widgets/<br>│       ├── todo_item.dart<br>│       └── todo_list.dart<br>└── main.dart</pre><p><strong>Structure Explanation:</strong></p><ul><li>data/ - Data layer (models, repositories, data sources)</li><li>logic/ - Business logic layer (BLoCs)</li><li>presentation/ - UI layer (pages, widgets)</li></ul><h3>Advanced Topics</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NTL4O-rZYCMKOqSHrLMPlw.png" /></figure><h3>1. BLoC to BLoC Communication</h3><p>When one BLoC needs data from another:</p><pre>class ProfileBloc extends Bloc&lt;ProfileEvent, ProfileState&gt; {<br>  final AuthBloc authBloc;<br>  late StreamSubscription authSubscription;<br>  <br>  ProfileBloc({required this.authBloc}) : super(ProfileInitial()) {<br>    // Listen to AuthBloc state changes<br>    authSubscription = authBloc.stream.listen((authState) {<br>      if (authState is AuthAuthenticated) {<br>        add(LoadProfile(authState.user.id));<br>      }<br>    });<br>  }<br>  <br>  @override<br>  Future&lt;void&gt; close() {<br>    authSubscription.cancel();<br>    return super.close();<br>  }<br>}</pre><h3>2. Hydrated BLoC — State Persistence</h3><p>Restore state when the app is reopened:</p><pre>class TodoBloc extends HydratedBloc&lt;TodoEvent, TodoState&gt; {<br>  TodoBloc() : super(TodoInitial());<br>  <br>  @override<br>  TodoState? fromJson(Map&lt;String, dynamic&gt; json) {<br>    try {<br>      final todos = (json[&#39;todos&#39;] as List)<br>          .map((e) =&gt; Todo.fromJson(e))<br>          .toList();<br>      return TodoLoaded(todos);<br>    } catch (_) {<br>      return null;<br>    }<br>  }<br>  <br>  @override<br>  Map&lt;String, dynamic&gt;? toJson(TodoState state) {<br>    if (state is TodoLoaded) {<br>      return {<br>        &#39;todos&#39;: state.todos.map((e) =&gt; e.toJson()).toList(),<br>      };<br>    }<br>    return null;<br>  }<br>}</pre><h3>3. Debouncing Events</h3><p>Prevent excessive API calls for features like search:</p><pre>class SearchBloc extends Bloc&lt;SearchEvent, SearchState&gt; {<br>  SearchBloc() : super(SearchInitial()) {<br>    on&lt;SearchTermChanged&gt;(<br>      (event, emit) async {<br>        emit(SearchLoading());<br>        try {<br>          final results = await repository.search(event.term);<br>          emit(SearchLoaded(results));<br>        } catch (e) {<br>          emit(SearchError(e.toString()));<br>        }<br>      },<br>      transformer: debounce(Duration(milliseconds: 500)),<br>    );<br>  }<br>}<br><br>EventTransformer&lt;E&gt; debounce&lt;E&gt;(Duration duration) {<br>  return (events, mapper) =&gt; events.debounceTime(duration).flatMap(mapper);<br>}</pre><h3>4. Throttling Events</h3><p>Limit event processing rate (useful for infinite scroll):</p><pre>on&lt;LoadMoreTodos&gt;(<br>  (event, emit) async {<br>    // Load more logic<br>  },<br>  transformer: throttle(Duration(seconds: 1)),<br>);<br><br>EventTransformer&lt;E&gt; throttle&lt;E&gt;(Duration duration) {<br>  return (events, mapper) =&gt; events.throttleTime(duration).flatMap(mapper);<br>}</pre><h3>When Should You Use BLoC?</h3><p><strong>Use BLoC when:</strong></p><ul><li>Building medium to large-scale applications</li><li>Working in a team environment</li><li>Testability is a priority</li><li>You need a clear separation of concerns</li><li>The app will scale and grow over time</li><li>You’re building for multiple platforms</li></ul><p><strong>Consider alternatives when:</strong></p><ul><li>Building a simple prototype or MVP</li><li>Working solo on a small project</li><li>Quick development is more important than structure</li><li>Your team is unfamiliar with reactive programming</li></ul><h3>Getting Started with BLoC</h3><h3>1. Add Dependencies</h3><pre>dependencies:<br>  flutter_bloc: ^8.1.3<br>  equatable: ^2.0.5<br>dev_dependencies:<br>  bloc_test: ^9.1.4<br>  mocktail: ^1.0.0</pre><h3>2. Install VS Code Extension</h3><p>Install the “Bloc” extension by Felix Angelov for automatic code generation.</p><h3>3. Generate BLoC Files</h3><p>Use the command palette (Cmd/Ctrl + Shift + P) and select “Bloc: New Bloc” to generate boilerplate code automatically.</p><h3>4. Learn from Official Resources</h3><ul><li>Official Documentation: <a href="https://bloclibrary.dev">https://bloclibrary.dev</a></li><li>GitHub Repository: <a href="https://github.com/felangel/bloc">https://github.com/felangel/bloc</a></li><li>Video Tutorials: Flutter Official Channel</li></ul><h3>Conclusion</h3><p>BLoC architecture is a powerful pattern that brings structure, testability, and scalability to Flutter applications. While it comes with a learning curve and requires more boilerplate code, the benefits far outweigh the costs for medium to large applications.</p><p>Key takeaways:</p><ul><li>BLoC separates business logic from UI completely</li><li>Events represent user actions, States represent UI conditions</li><li>The pattern makes testing straightforward and effective</li><li>It scales beautifully as your application grows</li><li>The repository pattern keeps data sources abstracted</li></ul><p>Start with a small feature, implement it with BLoC, and gradually expand. Once you understand the pattern, you’ll find it natural and highly productive.</p><p>Happy coding! 🚀</p><h3>Resources</h3><ul><li><a href="https://bloclibrary.dev/">Official BLoC Documentation</a></li><li><a href="https://pub.dev/packages/flutter_bloc">Flutter BLoC Package</a></li><li><a href="https://pub.dev/packages/bloc_test">Bloc Test Package</a></li><li><a href="https://github.com/felangel/bloc">Felix Angelov’s GitHub</a></li><li><a href="https://github.com/brianegan/flutter_architecture_samples">Flutter Architecture Samples</a></li></ul><p><em>If you found this guide helpful, please give it a clap and share it with fellow Flutter developers!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c2e8e50c5447" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Cloud Engineer’s Guide to 2026: Five Trends That Will Define Your Career]]></title>
            <link>https://thisarad404.medium.com/the-cloud-engineers-guide-to-2026-five-trends-that-will-define-your-career-d1f8483b332d?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/d1f8483b332d</guid>
            <category><![CDATA[mlops]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[aiops]]></category>
            <category><![CDATA[gitops]]></category>
            <category><![CDATA[platform-engineering]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sat, 24 Jan 2026 12:32:21 GMT</pubDate>
            <atom:updated>2026-01-24T12:32:21.730Z</atom:updated>
            <content:encoded><![CDATA[<p>The cloud industry is evolving faster than ever, and the skills that got you hired last year won’t be enough tomorrow. By 2026, the landscape will look dramatically different — driven by automation, intelligence, and a fundamental shift in how we build and operate cloud systems.</p><p>If you’re a cloud engineer, DevOps practitioner, or aspiring to enter this field, here’s what you need to know about the trends reshaping the industry and the skills that will make you invaluable.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dyn5l_NQh87BbQJXJe_Uvw.png" /></figure><h3>1. Platform Engineering Is Eating DevOps</h3><p>Remember when everyone wanted to hire DevOps engineers? The job market is shifting. By 2026, roughly 80% of large software organizations will adopt internal developer platforms (IDPs), fundamentally changing what employers seek in cloud talent.</p><p><strong>What’s changing:</strong> Companies are moving away from bespoke DevOps pipelines toward standardized, self-service platforms. Instead of each team maintaining their own deployment scripts, organizations are building centralized platform engineering layers that let developers deploy and manage environments independently.</p><p><strong>The GitOps revolution:</strong> This shift centers on GitOps — using Git as the single source of truth for code, infrastructure, configurations, and deployments. The numbers tell the story: 64% of organizations have already implemented GitOps, with most reporting higher reliability and faster rollbacks. Tools like Argo CD and Flux automatically sync your cluster state with your Git repository, making deployments consistent and recoverable.</p><p><strong>Why it matters:</strong> Platform engineers now earn approximately 20% more than traditional DevOps engineers. The role combines infrastructure expertise with product thinking — you’re not just building systems, you’re building platforms that other engineers use.</p><p><strong>Skills to develop:</strong></p><ul><li>GitOps workflows with Argo CD or Flux CD</li><li>Kubernetes as a platform foundation</li><li>Internal developer platform (IDP) design</li><li>Self-service infrastructure patterns</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Bp7_rL4AdNKFiuIZcWeqVQ.png" /></figure><h3>2. Infrastructure as Code Grows Up (Welcome to IaC 2.0)</h3><p>Writing Terraform scripts is table stakes. The industry is moving toward “Infrastructure as APIs” — what analysts call IaC 2.0.</p><p><strong>The new paradigm:</strong> Instead of writing monolithic infrastructure scripts for every project, modern teams treat infrastructure as programmable, reusable services. Define a VPC, database, or Kubernetes cluster once, then let applications request them like APIs.</p><p>Tools like Crossplane turn Kubernetes into a control plane for all your cloud resources, while Pulumi lets you write infrastructure in real programming languages (Python, TypeScript, Go) with full testing and CI/CD integration. This isn’t just about convenience — it’s about treating infrastructure with the same engineering rigor as application code.</p><p><strong>The evolution includes:</strong></p><ul><li>Policy-as-code enforcement (Open Policy Agent, Sentinel)</li><li>Automated testing of infrastructure changes</li><li>Full-featured programming languages instead of limited DSLs</li><li>Modular, versioned infrastructure libraries</li></ul><p><strong>Why it matters:</strong> Teams avoid repeatedly rewriting templates and gain on-demand, multi-cloud infrastructure that scales safely. Infrastructure becomes as dynamic and testable as your application code.</p><p><strong>Skills to develop:</strong></p><ul><li>Pulumi or AWS CDK for code-based IaC</li><li>Crossplane for Kubernetes-native infrastructure</li><li>Policy-as-code with OPA</li><li>Infrastructure testing frameworks</li></ul><h3>3. Observability Meets AI: The AIOps Explosion</h3><p>Logs and dashboards aren’t enough anymore. Modern cloud systems demand full observability — correlating metrics, logs, and traces end-to-end to truly “see inside” distributed services.</p><p><strong>The observability stack:</strong> Teams are standardizing on tools like Prometheus for metrics, Grafana for visualization, and OpenTelemetry for distributed tracing. The payoff is significant: mature observability can cut mean time to recovery by approximately 40%.</p><p><strong>But here’s where it gets interesting:</strong> AI is transforming operations. AIOps adoption jumped from 42% in 2024 to 54% in 2025, and the trend is accelerating.</p><p><strong>What AI brings to ops:</strong></p><ul><li>Automatic anomaly detection across millions of metrics</li><li>Predictive failure analysis before outages occur</li><li>AI-assisted root cause analysis</li><li>Automated remediation for common issues</li><li>Dynamic resource scaling based on predicted load</li></ul><p>Some teams are already using AI to auto-fix security vulnerabilities or scale infrastructure proactively. Tools like Datadog Watchdog, Dynatrace, and cloud-native AIOps features are moving teams from reactive firefighting to proactive operations.</p><p><strong>Why it matters:</strong> AI-enabled observability has been linked to halving outage costs and drastically improving uptime. In 2026’s complex distributed systems, human-only monitoring simply won’t scale.</p><p><strong>Skills to develop:</strong></p><ul><li>Prometheus, Grafana, and OpenTelemetry</li><li>Distributed tracing patterns</li><li>AI-powered monitoring platforms (Datadog, Dynatrace, New Relic)</li><li>Incident management with AI-assisted workflows</li></ul><h3>4. AI Infrastructure: The New Cloud Frontier</h3><p>If you’re not thinking about AI infrastructure, you’re already behind. Global AI infrastructure spending will exceed $2 trillion by 2026, and over half of enterprises will deploy AI agents by 2027.</p><p><strong>The MLOps challenge:</strong> Deploying AI models in production requires an entirely new skillset — provisioning GPU clusters, serving models with low latency, monitoring model drift, optimizing inference costs. It’s cloud engineering meets machine learning operations.</p><p><strong>What this looks like in practice:</strong></p><ul><li>Containerizing ML models for production deployment</li><li>Scheduling GPU workloads on Kubernetes</li><li>Managing model serving with tools like NVIDIA Triton or KServe</li><li>Building real-time inference pipelines</li><li>Operating vector databases and feature stores</li></ul><p>Kubernetes itself is adapting to AI workloads, with GPU scheduling and specialized operators becoming standard. Cloud platforms are racing to offer managed ML services (AWS SageMaker, Azure ML, Google Vertex), but enterprises still need engineers who understand both cloud and ML to orchestrate it all.</p><p><strong>Why it matters:</strong> MLOps job openings grew approximately 10-fold over five years. As businesses embed AI in products — from chatbots to recommendation engines to autonomous systems — they desperately need engineers who can bridge cloud infrastructure and machine learning.</p><p><strong>Skills to develop:</strong></p><ul><li>Kubernetes with GPU node management</li><li>Model serving frameworks (Triton, KServe, Kubeflow)</li><li>ML orchestration tools like Ray</li><li>Understanding of model performance and retraining pipelines</li><li>Vector databases and ML data infrastructure</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*3YqsOJYgWBfZUt6wHYc-Nw.png" /></figure><h3>5. Event-Driven and Serverless: The New Default</h3><p>Synchronous request-response architectures are giving way to event-driven systems. By 2026, asynchronous communication will be the default for high-scale applications.</p><p><strong>The pattern:</strong> Applications break into decoupled services that react to events — user actions, data changes, system triggers — through message queues and event buses. Platforms like Apache Kafka, AWS EventBridge, and Google Pub/Sub enable services to publish and subscribe to event streams.</p><p>Serverless functions amplify this: AWS Lambda, Google Cloud Functions, and Azure Functions automatically execute code in response to events, with sub-10-minute deployment times and pay-per-use pricing. Serverless usage jumped 25% in 2024 alone.</p><p><strong>Real-world applications:</strong></p><ul><li>CI/CD pipelines triggered by Git events</li><li>Real-time data processing streams</li><li>On-demand AI inference triggered by data events</li><li>Automated incident response workflows</li></ul><p>Teams using event-driven orchestration report cutting mean time to fix from hours to minutes.</p><p><strong>Why it matters:</strong> Event-driven and serverless architectures deliver better performance, reliability, and cost efficiency. For high-volume microservices, IoT pipelines, or systems with variable load, this pattern is becoming essential.</p><p><strong>Skills to develop:</strong></p><ul><li>Event streaming platforms (Kafka, RabbitMQ)</li><li>Serverless function development and optimization</li><li>Event-driven architecture patterns</li><li>Workflow orchestration (Argo Workflows, Tekton)</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kT7BnAJFnW3_6NggVvf21g.png" /></figure><h3>Regional Perspectives: Where the Opportunities Are</h3><p>Cloud adoption isn’t uniform globally, and understanding regional trends can guide your career strategy.</p><p><strong>North America</strong> remains the largest market, with leading innovation in platform engineering and AI projects. IDP/GitOps and AIOps skills are highly sought in tech hubs like Silicon Valley, Seattle, and New York.</p><p><strong>Europe</strong> shows strong adoption with heightened focus on security and compliance. Companies invest heavily in internal cloud platforms to balance agility with strict data regulations.</p><p><strong>Asia-Pacific</strong> is experiencing explosive growth and leads in AI adoption. Nearly 90% of APAC enterprises now run multi-cloud workloads, and organizations there often embrace cutting-edge trends (serverless, service mesh, WebAssembly) faster than Western counterparts.</p><p><strong>Emerging markets</strong> in Latin America, Middle East, and Africa are accelerating cloud adoption from a smaller base, driven by government cloud initiatives and digital transformation programs.</p><h3>The Bottom Line: What to Learn Now</h3><p>The most in-demand cloud roles for 2026 include:</p><p><strong>Platform/DevOps Engineers</strong> with platform engineering focus — designing IDPs and GitOps workflows. These roles now rival classic DevOps jobs in frequency.</p><p><strong>Cloud SREs</strong> specializing in observability and AIOps — managing complex distributed systems with AI-augmented tools.</p><p><strong>Machine Learning/AI Ops Engineers</strong> — bridging cloud infrastructure and ML model deployment at scale.</p><p><strong>Cloud Software Engineers</strong> skilled in serverless and event-driven design — building the next generation of scalable applications.</p><p>All these roles command premium salaries, with platform engineers earning significantly more than traditional DevOps practitioners.</p><h3>Start Today</h3><p>The cloud industry rewards those who stay ahead of the curve. Basic containerization and simple CI/CD are now baseline expectations. The engineers who thrive in 2026 will be those who master platform thinking, embrace AI-powered operations, and understand how to build self-service infrastructure at scale.</p><p>The trends are clear. The tools are available. The question is: are you ready to level up?</p><p><em>What cloud skills are you focusing on for 2026? Share your thoughts in the comments below.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d1f8483b332d" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Localhost to Production: How I Migrated My Full-Stack App (And The Mistakes I Made)]]></title>
            <link>https://thisarad404.medium.com/from-localhost-to-production-how-i-migrated-my-full-stack-app-and-the-mistakes-i-made-fb76b8cebbc8?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/fb76b8cebbc8</guid>
            <category><![CDATA[dns]]></category>
            <category><![CDATA[cors]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[production-deployment]]></category>
            <category><![CDATA[nginx]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Thu, 22 Jan 2026 15:56:44 GMT</pubDate>
            <atom:updated>2026-01-22T15:56:44.207Z</atom:updated>
            <content:encoded><![CDATA[<p><strong>By Thisara Dasun</strong></p><p>Deploying a full-stack application is easy when you stick to one platform. But what happens when you want to optimize costs and performance by splitting your architecture?</p><p>Recently, I migrated my application — a comprehensive mobile distribution system — from a simple development setup to a professional production architecture. I decided to host my <strong>Frontend on Render</strong> (for easy CD/CI) and my <strong>Backend on a Linux VPS</strong> (for full control and performance).</p><p>It sounds great on paper, but I hit several roadblocks involving DNS configuration, SSL termination, and the dreaded CORS errors.</p><p>Here is the story of my migration, the embarrassing mistakes I made, and how I finally fixed them.</p><h3>🏗️ The Goal Architecture</h3><p>Instead of a monolithic deployment, I wanted a “Headless” approach:</p><ol><li><strong>Frontend (React):</strong> Hosted on <strong>Render</strong>. It serves the UI and connects to the backend API.</li><li><strong>Backend (Node.js/Docker):</strong> Hosted on a <strong>Linux VPS</strong>. It handles the database, business logic, and API requests.</li><li><strong>Domain:</strong> One custom domain (awesome-startup.com) to rule them all.</li></ol><p>The target URLs:</p><ul><li>Frontend: <a href="https://awesome-startup.com">https://awesome-startup.com</a></li><li>Backend: <a href="https://api.awesome-startup.com">https://api.awesome-startup.com</a></li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0vbRkZaDe93-2ghkpLwolA.png" /></figure><h3>🚫 The “Oops” Moments (My Mistakes)</h3><p>If you are new to DevOps, you might recognize some of these.</p><h3>Mistake 1: The “Forwarding” Trap</h3><p>I bought my domain on GoDaddy and saw a feature called <strong>“Forwarding”</strong>. I thought, “Great! I’ll just forward awesome-startup.com to my Render URL.”</p><p>Why it failed: Forwarding just redirects the user. The browser address bar changed from my custom domain to myapp.onrender.com. It looked unprofessional and messed up my cookies.</p><p>Lesson: Never use Forwarding for a production site. Always use DNS Records.</p><h3>Mistake 2: The “Split Brain” DNS</h3><p>I wanted my domain to work for both the frontend and backend, so I pointed the root record (@) to Render’s IP… and <em>also</em> to my VPS IP.</p><p>Why it failed: DNS doesn’t work like a load balancer. The internet didn’t know which server to send traffic to. Sometimes the site loaded (Render), sometimes it showed a 404 (VPS).</p><p>Lesson: The root domain (@) can only point to one server. You must use subdomains (like api.) for the second server.</p><h3>Mistake 3: The Double CORS Nightmare</h3><p>This was the hardest to debug. I configured Nginx on my VPS to add Access-Control-Allow-Origin headers. But I <em>also</em> had my Node.js app (using the cors package) adding the same headers.</p><p>The Result: The browser received two sets of headers:</p><p>Access-Control-Allow-Origin: <a href="https://awesome-startup.com,">https://awesome-startup.com,</a> <a href="https://awesome-startup.com">https://awesome-startup.com</a></p><p>Chrome immediately blocked the request because duplicate headers are invalid.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*I_qMO__ZbkuWJdynP8XT7Q.png" /></figure><h3>✅ The Solution: A Clean, Secure Setup</h3><p>Here is the step-by-step configuration that finally worked.</p><h3>Step 1: DNS Configuration (The Roadmap)</h3><p>I cleaned up my DNS records to strictly separate traffic.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*cctB52PZ2OhEN3FXNj_sLw.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UyLBGwe8MmDe2LeZGTp8KQ.png" /></figure><h3>Step 2: Nginx Reverse Proxy (The Traffic Cop)</h3><p>On the VPS, I used Nginx to listen for the api subdomain and forward traffic to my Docker container running on port 5000.</p><p><strong>Crucial detail:</strong> I removed all manual CORS headers from Nginx and let the application handle it.</p><blockquote><strong>server {<br> server_name api.awesome-startup.com;</strong></blockquote><blockquote><strong>location / {<br> proxy_pass [http://127.0.0.1:5000](http://127.0.0.1:5000);<br> <br> # Standard Proxy Headers<br> proxy_set_header Host $host;<br> proxy_set_header X-Real-IP $remote_addr;<br> proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;<br> proxy_set_header X-Forwarded-Proto $scheme;<br> <br> # NOTE: No CORS headers here!<br> }<br>}</strong></blockquote><p>Then, I used Certbot to automatically generate SSL certificates:</p><p>sudo certbot — nginx -d api.awesome-startup.com</p><h3>Step 3: Connecting the App (.env)</h3><p>Finally, I had to tell my backend who to trust. Inside my Docker environment variables, I set the CORS origin to my new frontend domain.</p><p>NODE_ENV=production<br>CORS_ORIGIN=[https://awesome-startup.com](https://awesome-startup.com)</p><h3>🚀 The Result</h3><ul><li><strong>Security:</strong> Both Frontend and Backend are fully encrypted (HTTPS).</li><li><strong>Performance:</strong> The frontend is served via Render’s CDN, while the backend runs on dedicated compute power.</li><li><strong>Professionalism:</strong> Users never see an IP address or a duckdns URL. They only see awesome-startup.com.</li></ul><p>Moving from localhost to production is rarely a straight line. It involves understanding how the internet actually routes traffic (DNS), how servers talk to each other (Nginx/Proxy), and how browsers protect users (CORS).</p><p>Hopefully, sharing my “oops” moments helps you avoid them in your next deployment!</p><p>#DevOps #WebDevelopment #React #NodeJS #Docker #Nginx #Deployment #FullStack</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=fb76b8cebbc8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[DevOps Problem-Solving: From Backup Automation to Infrastructure Deployment]]></title>
            <link>https://thisarad404.medium.com/devops-problem-solving-from-backup-automation-to-infrastructure-deployment-171ed07fa6a0?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/171ed07fa6a0</guid>
            <category><![CDATA[terraform-cloud]]></category>
            <category><![CDATA[tomcat]]></category>
            <category><![CDATA[java]]></category>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[terraform]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sat, 16 Aug 2025 20:13:28 GMT</pubDate>
            <atom:updated>2025-08-16T20:13:28.307Z</atom:updated>
            <content:encoded><![CDATA[<h4>In the fast-paced world of DevOps, teams constantly face complex technical challenges that require innovative solutions. This blog explores real-world scenarios from the xFusionCorp Industries and Nautilus DevOps teams, highlighting how they tackled automation, infrastructure management, and deployment challenges.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*YlVpw7rSSbNYI5oehgxtBw.png" /></figure><h3>Challenge 1: Automated Website Backup System</h3><h3>The Problem</h3><p>The xFusionCorp Industries production support team needed to create an automated backup solution for their static website running on App Server 1. The requirements were comprehensive:</p><ul><li>Create a bash script for automated backups</li><li>Generate zip archives of the /var/www/html/beta directory</li><li>Store backups both locally and on a remote backup server</li><li>Ensure password-less file transfers</li><li>Make the solution executable by the appropriate server user</li></ul><h3>The Solution Approach</h3><h4>Script Architecture</h4><p>The team developed a comprehensive bash script (beta_backup.sh) that addressed all requirements:</p><pre>#!/bin/bash<br># Website Backup Script for xFusionCorp Industries<br><br># Variables for configuration<br>SOURCE_DIR=&quot;/var/www/html/beta&quot;<br>ARCHIVE_NAME=&quot;xfusioncorp_beta.zip&quot;<br>LOCAL_BACKUP_DIR=&quot;/backup&quot;<br>BACKUP_SERVER=&quot;stbkp01.stratos.xfusioncorp.com&quot;<br>BACKUP_SERVER_USER=&quot;clint&quot;<br>REMOTE_BACKUP_DIR=&quot;/backup&quot;<br>LOG_FILE=&quot;/var/log/beta_backup.log&quot;<br># Logging function for audit trails<br>log_message() {<br>    echo &quot;$(date &#39;+%Y-%m-%d %H:%M:%S&#39;) - $1&quot; | tee -a &quot;$LOG_FILE&quot;<br>}</pre><h4>Key Implementation Features</h4><p><strong>1. Error Handling and Validation</strong></p><ul><li>Source directory existence checks</li><li>Backup directory creation with proper permissions</li><li>Archive creation validation</li><li>File size reporting for verification</li></ul><p><strong>2. Security Implementation</strong></p><ul><li>SSH key-based authentication for password-less transfers</li><li>Proper file permissions (600 for private keys)</li><li>User-specific ownership settings</li></ul><p><strong>3. Logging and Monitoring</strong></p><ul><li>Comprehensive logging to /var/log/beta_backup.log</li><li>Timestamped entries for audit trails</li><li>Success/failure status reporting</li></ul><h4>Deployment Process</h4><p>The implementation followed a structured approach:</p><ol><li><strong>Environment Setup</strong></li></ol><p># Create scripts directory</p><p>sudo mkdir -p /scripts</p><p># Set proper ownership</p><p>sudo chown tony:tony /scripts/beta_backup.sh</p><p>sudo chmod +x /scripts/beta_backup.sh</p><ol><li><strong>SSH Key Configuration</strong></li></ol><p># Generate SSH key pair</p><p>ssh-keygen -t rsa -b 2048 -f ~/.ssh/id_rsa -N &quot;&quot;</p><p># Copy public key to backup server</p><p>ssh-copy-id clint@stbkp01.stratos.xfusioncorp.com</p><ol><li><strong>Testing and Validation</strong></li></ol><ul><li>Local backup verification</li><li>Remote backup confirmation</li><li>Log file analysis</li></ul><h3>Results and Benefits</h3><p>The solution achieved:</p><ul><li><strong>100% automation</strong> of the backup process</li><li><strong>Zero password prompts</strong> during execution</li><li><strong>Comprehensive logging</strong> for troubleshooting</li><li><strong>Scalable architecture</strong> for multiple websites</li><li><strong>Audit-ready documentation</strong> of all backup operations</li></ul><h3>Challenge 2: Cloud Infrastructure Migration with Terraform</h3><h3>The Problem</h3><p>The Nautilus DevOps team faced a complex AWS cloud migration project. Rather than attempting a massive single migration, they adopted an incremental approach, starting with fundamental infrastructure components like SSH key pair management.</p><p><strong>Requirements:</strong></p><ul><li>Create an RSA key pair named “datacenter-kp”</li><li>Save the private key to /home/bob/datacenter-kp.pem</li><li>Use Terraform for infrastructure as code</li><li>Ensure proper security configurations</li></ul><h3>The Solution Journey</h3><h4>Initial Challenges</h4><p>The team encountered several technical hurdles:</p><ol><li><strong>Provider Configuration Conflicts</strong></li></ol><ul><li>Duplicate required providers blocks</li><li>Missing TLS and local provider dependencies</li></ul><p><strong>2. Resource Type Confusion</strong></p><ul><li>Initially created only local TLS keys</li><li>Validation system expected AWS key pair resources</li></ul><h4>Final Working Solution</h4><pre># Generate RSA private key<br>resource &quot;tls_private_key&quot; &quot;datacenter_key&quot; {<br>  algorithm = &quot;RSA&quot;<br>  rsa_bits  = 2048<br>}<br><br># Create AWS Key Pair using the generated public key<br>resource &quot;aws_key_pair&quot; &quot;datacenter_kp&quot; {<br>  key_name   = &quot;datacenter-kp&quot;<br>  public_key = tls_private_key.datacenter_key.public_key_openssh<br>}<br><br># Save the private key to file<br>resource &quot;local_file&quot; &quot;private_key&quot; {<br>  content  = tls_private_key.datacenter_key.private_key_pem<br>  filename = &quot;/home/bob/datacenter-kp.pem&quot;<br>  file_permission = &quot;0600&quot;<br>}</pre><h4>Key Learning Points</h4><p><strong>1. Infrastructure as Code Best Practices</strong></p><ul><li>Clear separation of concerns between providers</li><li>Proper resource dependencies</li><li>Security-first approach with file permissions</li></ul><p><strong>2. Validation Requirements Understanding</strong></p><ul><li>AWS resources vs. local resources</li><li>Proper naming conventions</li><li>Type specifications (“rsa” type requirement)</li></ul><p><strong>3. Provider Configuration Management</strong></p><pre>terraform {<br>  required_providers {<br>    aws = {<br>      source = &quot;hashicorp/aws&quot;<br>      version = &quot;5.91.0&quot;<br>    }<br>    tls = {<br>      source  = &quot;hashicorp/tls&quot;<br>      version = &quot;~&gt; 4.0&quot;<br>    }<br>    local = {<br>      source  = &quot;hashicorp/local&quot;<br>      version = &quot;~&gt; 2.4&quot;<br>    }<br>  }<br>}</pre><h3>Challenge 3: Java Application Deployment with Tomcat</h3><h3>The Problem</h3><p>The application development team needed to deploy a Java-based application (ROOT.war) on App Server 2 using Tomcat, with specific requirements:</p><ul><li>Custom port configuration (5001)</li><li>Proper application deployment</li><li>Direct base URL access</li></ul><h3>The Solution Implementation</h3><h4>Installation and Configuration Process</h4><p><strong>1. Environment Preparation</strong></p><pre># Java installation verification<br>java -version<br><br># Tomcat download and extraction<br>wget https://archive.apache.org/dist/tomcat/tomcat-9/v9.0.75/bin/apache-tomcat-9.0.75.tar.gz<br>sudo tar -xzf apache-tomcat-9.0.75.tar.gz<br>sudo mv apache-tomcat-9.0.75 /opt/tomcat</pre><p><strong>2. Security Configuration</strong></p><pre># Create dedicated tomcat user<br>sudo useradd -r -m -U -d /opt/tomcat -s /bin/false tomcat<br><br># Set proper ownership and permissions<br>sudo chown -R tomcat:tomcat /opt/tomcat/<br>sudo chmod +x /opt/tomcat/bin/*.sh</pre><p><strong>3. Service Configuration</strong> Created a systemd service for proper process management:</p><pre>[Unit]<br>Description=Apache Tomcat Web Application Container<br>After=network.target<br><br>[Service]<br>Type=forking<br>Environment=JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk<br>Environment=CATALINA_HOME=/opt/tomcat<br>Environment=CATALINA_BASE=/opt/tomcat<br>ExecStart=/opt/tomcat/bin/startup.sh<br>ExecStop=/opt/tomcat/bin/shutdown.sh<br>User=tomcat<br>Group=tomcat</pre><p><strong>4. Application Deployment</strong></p><pre># Deploy ROOT.war application<br>sudo cp /tmp/ROOT.war /opt/tomcat/webapps/<br>sudo chown tomcat:tomcat /opt/tomcat/webapps/ROOT.war<br><br># Start and enable service<br>sudo systemctl enable tomcat<br>sudo systemctl start tomcat</pre><h3>Key Takeaways and Best Practices</h3><h3>1. Automation First Approach</h3><ul><li><strong>Comprehensive scripting</strong> eliminates human error</li><li><strong>Logging and monitoring</strong> provide visibility</li><li><strong>Error handling</strong> ensures reliability</li></ul><h3>2. Security by Design</h3><ul><li><strong>SSH key authentication</strong> over password-based access</li><li><strong>Proper file permissions</strong> (600 for private keys)</li><li><strong>Dedicated service users</strong> with minimal privileges</li></ul><h3>3. Infrastructure as Code Benefits</h3><ul><li><strong>Version control</strong> for infrastructure changes</li><li><strong>Reproducible deployments</strong> across environments</li><li><strong>Validation and testing</strong> before production</li></ul><h3>4. Incremental Migration Strategy</h3><ul><li><strong>Break complex tasks</strong> into manageable components</li><li><strong>Test each component</strong> independently</li><li><strong>Build confidence</strong> through small successes</li></ul><h3>5. Troubleshooting Methodology</h3><ul><li><strong>Systematic approach</strong> to problem identification</li><li><strong>Logging analysis</strong> for root cause determination</li><li><strong>Iterative testing</strong> until resolution</li></ul><h3>Conclusion</h3><p>These real-world scenarios demonstrate the complexity and variety of challenges faced in modern DevOps environments. The solutions showcase the importance of:</p><ul><li><strong>Thorough planning and requirement analysis</strong></li><li><strong>Security-first implementation approaches</strong></li><li><strong>Comprehensive testing and validation</strong></li><li><strong>Documentation and logging for maintenance</strong></li></ul><p>By following these patterns and practices, DevOps teams can build robust, scalable, and maintainable infrastructure solutions that support business growth and operational excellence.</p><p>The key to success lies in understanding that each challenge is an opportunity to improve processes, enhance automation, and build more resilient systems. Whether it’s backup automation, infrastructure provisioning, or application deployment, the principles of careful planning, security consciousness, and systematic implementation remain constant across all scenarios.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=171ed07fa6a0" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Securing Apache with iptables: A Complete Guide to Firewall Configuration]]></title>
            <link>https://thisarad404.medium.com/securing-apache-with-iptables-a-complete-guide-to-firewall-configuration-373d971efe89?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/373d971efe89</guid>
            <category><![CDATA[devops]]></category>
            <category><![CDATA[iptables]]></category>
            <category><![CDATA[manage-linux-iptables]]></category>
            <category><![CDATA[apache]]></category>
            <category><![CDATA[docker]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sat, 16 Aug 2025 19:36:33 GMT</pubDate>
            <atom:updated>2025-08-16T19:36:33.868Z</atom:updated>
            <content:encoded><![CDATA[<p>As infrastructure security becomes increasingly critical, protecting web applications from unauthorized access is a top priority. Recently, I had to secure our Nautilus infrastructure running in Stratos DC, where Apache servers on port 6200 were exposed to the entire network without any firewall protection. Here’s how I implemented a comprehensive security solution using iptables.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DBPwIVpofHwDULgzTZ4PWA.png" /></figure><h3>The Challenge</h3><p>Our security team identified a significant vulnerability:</p><ul><li><strong>Apache running on port 6200</strong> across multiple app servers</li><li><strong>No firewall protection</strong> — port accessible from anywhere</li><li><strong>Need selective access</strong> — only the Load Balancer (LBR) should reach the app servers</li><li><strong>Persistence required</strong> — rules must survive system reboots</li></ul><h3>Infrastructure Overview</h3><p>The environment consisted of:</p><ul><li><strong>3 Application Servers</strong>: stapp01, stapp02, stapp03 (CentOS Stream 9)</li><li><strong>1 Load Balancer</strong>: stlb01 (172.16.238.14)</li><li><strong>1 Jump Host</strong>: For administrative access</li><li><strong>Apache running on port 6200</strong> on all app servers</li></ul><h3>Step-by-Step Implementation</h3><h3>Phase 1: Installing iptables</h3><p>First, I needed to install iptables and its services on all app servers. Modern CentOS systems often use firewalld, but for this specific requirement, iptables provided the granular control we needed.</p><pre># SSH to each app server<br>ssh tony@stapp01    # Password: Ir0nM@n<br>ssh steve@stapp02   # Password: Am3ric@<br>ssh banner@stapp03  # Password: BigGr33n</pre><pre># Install iptables and iptables-services<br>sudo yum install -y iptables iptables-services</pre><pre># Enable and start the service<br>sudo systemctl enable iptables<br>sudo systemctl start iptables</pre><p><strong>Key Learning</strong>: Always install iptables-services package - it provides the systemd service files needed for persistence and automatic startup.</p><h3>Phase 2: Designing the Security Rules</h3><p>The security policy required:</p><ol><li><strong>Allow SSH (port 22)</strong> from anywhere for administrative access</li><li><strong>Allow port 6200 only from LBR</strong> (172.16.238.14)</li><li><strong>Block port 6200 from all other sources</strong></li><li><strong>Maintain existing connectivity</strong> for established connections</li></ol><p>Here’s the rule configuration I implemented:</p><pre># Clear existing rules and set permissive defaults<br>sudo iptables -F<br>sudo iptables -P INPUT ACCEPT<br>sudo iptables -P FORWARD ACCEPT<br>sudo iptables -P OUTPUT ACCEPT</pre><pre># Allow loopback traffic (essential for system processes)<br>sudo iptables -A INPUT -i lo -j ACCEPT</pre><pre># Allow established and related connections<br>sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT</pre><pre># Allow SSH from anywhere<br>sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT</pre><pre># CRITICAL: Allow port 6200 ONLY from LBR host<br>sudo iptables -A INPUT -p tcp --dport 6200 -s 172.16.238.14 -j ACCEPT</pre><pre># Block port 6200 from all other sources<br>sudo iptables -A INPUT -p tcp --dport 6200 -j DROP</pre><pre># Save rules for persistence<br>sudo service iptables save</pre><h3>Phase 3: The Critical Issue I Encountered</h3><p>During the initial setup on stapp01, I encountered an unexpected problem:</p><pre>sudo systemctl status httpd<br>● httpd.service - The Apache HTTP Server<br>   Active: failed (Result: exit-code)<br>   <br>Aug 16 18:23:43 stapp01 httpd[510]: (98)Address already in use: <br>AH00072: make_sock: could not bind to address 0.0.0.0:3000</pre><p><strong>The Problem</strong>: Port 3000 (not 6200!) was already in use by sendmail, preventing Apache from starting.</p><p><strong>The Solution</strong>:</p><pre># Identify the conflicting process<br>sudo netstat -tlnp | grep :3000<br>tcp 0 0 127.0.0.1:3000 0.0.0.0:* LISTEN 449/sendmail: accep</pre><pre># Stop the conflicting service<br>sudo systemctl stop sendmail<br>sudo systemctl disable sendmail</pre><pre># Start Apache successfully<br>sudo systemctl start httpd<br>sudo systemctl enable httpd</pre><p><strong>Lesson Learned</strong>: Always check for port conflicts before assuming firewall issues. Use netstat -tlnp to identify processes using specific ports.</p><h3>Phase 4: Rule Order Matters</h3><p>The order of iptables rules is crucial. My rules were structured as:</p><ol><li><strong>Allow loopback</strong> (essential system traffic)</li><li><strong>Allow established connections</strong> (don’t break existing sessions)</li><li><strong>Allow SSH</strong> (maintain administrative access)</li><li><strong>Allow specific port from specific source</strong> (targeted access)</li><li><strong>Block specific port from everywhere else</strong> (security enforcement)</li></ol><p>This order ensures that legitimate traffic flows through while blocking unauthorized access.</p><h3>Phase 5: Testing and Verification</h3><p>Testing revealed the effectiveness of the configuration:</p><p><strong>From Jump Host (unauthorized source)</strong>:</p><pre>telnet stapp01 6200<br>Trying 172.16.238.10...<br>telnet: connect to address 172.16.238.10: Connection timed out ✓</pre><p><strong>From LBR Host (authorized source)</strong>:</p><pre>telnet stapp01 6200<br>Trying 172.16.238.10...<br>Connected to stapp01. ✓</pre><p>The HTTP responses from the app servers confirmed Apache was running and accessible only from the designated source.</p><h3>Key Troubleshooting Tips</h3><h3>1. Port Conflict Resolution</h3><pre># Always check what&#39;s using a port<br>sudo netstat -tlnp | grep :PORT_NUMBER<br># or if lsof is available<br>sudo lsof -i :PORT_NUMBER</pre><h3>2. Rule Verification</h3><pre># View rules with line numbers<br>sudo iptables -L -n --line-numbers<br># Check specific chains<br>sudo iptables -L INPUT -n</pre><h3>3. Service Status Checks</h3><pre># Verify iptables service<br>sudo systemctl status iptables<br># Check rule persistence<br>sudo cat /etc/sysconfig/iptables</pre><h3>Best Practices Learned</h3><h3>1. Always Plan for SSH Access</h3><p>Never block SSH without an alternative access method. Always include:</p><pre>sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT</pre><h3>2. Test Before Implementing</h3><p>Use temporary rules first, then make them persistent:</p><pre># Test first (temporary)<br>sudo iptables -A INPUT -p tcp --dport 6200 -s 172.16.238.14 -j ACCEPT<br># Only save when confirmed working<br>sudo service iptables save</pre><h3>3. Document Your Rules</h3><p>Always document the purpose of each rule:</p><pre># Allow HTTP from LBR only - Security requirement SR-2025-001<br>sudo iptables -A INPUT -p tcp --dport 6200 -s 172.16.238.14 -j ACCEPT</pre><h3>4. Monitor After Implementation</h3><p>Set up monitoring to detect any issues:</p><pre># Check dropped connections<br>sudo iptables -L -v -n | grep DROP</pre><h3>Results and Impact</h3><p>The implementation successfully achieved all security objectives:</p><ul><li>✅ <strong>Port 6200 secured</strong>: Only accessible from authorized LBR host</li><li>✅ <strong>Zero service disruption</strong>: All legitimate traffic maintained</li><li>✅ <strong>Persistent configuration</strong>: Rules survive system reboots</li><li>✅ <strong>Administrative access preserved</strong>: SSH remains accessible</li></ul><h3>Performance Considerations</h3><p>The iptables rules added minimal overhead:</p><ul><li><strong>Rule processing</strong>: O(n) complexity, but with only 5–6 rules per server</li><li><strong>Memory usage</strong>: Negligible impact on system resources</li><li><strong>Network latency</strong>: No measurable increase in response times</li></ul><h3>Conclusion</h3><p>Implementing selective firewall access using iptables proved to be an effective security measure. The key to success was:</p><ol><li><strong>Thorough planning</strong> of rule structure and order</li><li><strong>Identifying and resolving</strong> port conflicts proactively</li><li><strong>Comprehensive testing</strong> from both authorized and unauthorized sources</li><li><strong>Proper persistence</strong> configuration to ensure rules survive reboots</li></ol><p>This implementation demonstrates that with careful planning and execution, significant security improvements can be achieved without impacting legitimate system functionality. The selective access control now ensures that our Apache servers are protected while maintaining the necessary connectivity for our load balancer infrastructure.</p><h3>Next Steps</h3><p>Future enhancements could include:</p><ul><li><strong>Logging dropped connections</strong> for security monitoring</li><li><strong>Rate limiting</strong> to prevent brute force attacks</li><li><strong>Integration with monitoring systems</strong> for alert generation</li><li><strong>Automated backup</strong> of iptables configurations</li></ul><p>The foundation is now in place for a more secure and manageable infrastructure that meets both security and operational requirements.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=373d971efe89" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Level Up Your ML Game: Must-Follow LinkedIn Influencers]]></title>
            <link>https://thisarad404.medium.com/level-up-your-ml-game-must-follow-linkedin-influencers-4e15a7c99f00?source=rss-3a1f95f999cb------2</link>
            <guid isPermaLink="false">https://medium.com/p/4e15a7c99f00</guid>
            <category><![CDATA[influencers]]></category>
            <category><![CDATA[large-language-models]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[machine-learning]]></category>
            <category><![CDATA[linkedin-marketing]]></category>
            <dc:creator><![CDATA[Thisara Dasun]]></dc:creator>
            <pubDate>Sat, 16 Aug 2025 09:20:35 GMT</pubDate>
            <atom:updated>2025-08-16T09:20:35.995Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*KN_Vehmzq44Bydk8cfZI6g.png" /></figure><p>Want to stay ahead in the fast-paced world of machine learning? LinkedIn is a goldmine of knowledge, and following the right people can be a game-changer for your learning journey and career path. Based on our recent research, we’ve compiled a list of top machine learning influencers you should definitely have in your feed. Forget just names; we’re diving into <em>what</em> makes them influential and <em>why</em> you should care.</p><p>Deep Research Link: <a href="https://docs.google.com/document/d/1hQdwpCllghWI04eAbhdvNhXVCDcmc0xGWzjcvWuM4FE/edit?usp=sharing">https://docs.google.com/document/d/1hQdwpCllghWI04eAbhdvNhXVCDcmc0xGWzjcvWuM4FE/edit?usp=sharing</a></p><h3>The Foundational Thinkers: Shaping the Future of AI</h3><p>These are the pioneers who laid the groundwork for modern AI:</p><ul><li><strong>Andrew Ng</strong> 🧑‍🏫 (<a href="https://www.google.com/search?q=https://linkedin.com/in/andrewng"><strong>linkedin.com/in/andrewng</strong></a>): The master educator! Follow him for accessible explanations of complex AI concepts, practical applications, and insights into the latest advancements. His work democratizing AI education is unparalleled.</li><li><strong>Yann LeCun</strong> 🧠 (<a href="https://www.google.com/search?q=https://linkedin.com/in/yannlecun"><strong>linkedin.com/in/yannlecun</strong></a>): A true visionary and one of the “Godfathers of AI.” Expect cutting-edge research, theoretical discussions, and his perspective on the societal impact of AI.</li><li><strong>Fei-Fei Li</strong>: A champion for human-centered AI and the creator of ImageNet. Her insights blend technical expertise with a focus on ethical and responsible AI development.</li><li><strong>Demis Hassabis</strong>: The mind behind Google DeepMind. Follow him to see AI’s potential in tackling complex challenges, from healthcare to achieving artificial general intelligence.</li></ul><p><strong>Why follow them?</strong> These individuals provide a deep understanding of the fundamental principles and the long-term vision of AI. They’re essential for grasping the “why” behind the “how.”</p><h3>The Builders: Turning Theory into Reality</h3><p>These are the hands-on experts who are building and deploying ML systems:</p><ul><li><strong>Andrej Karpathy</strong>: Known for his practical approach and ability to explain complex topics simply. His content, including his popular YouTube tutorials, focuses on applied AI and MLOps.</li><li><strong>Soumith Chintala</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/soumithchintala"><strong>linkedin.com/in/soumithchintala</strong></a>): Co-creator of PyTorch, a cornerstone of modern deep learning. Following him gives you a direct line to the evolution of crucial ML tools.</li><li><strong>Mike Tamir</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/miketamir"><strong>linkedin.com/in/miketamir</strong></a>): Bridges the gap between academia and industry, offering insights into real-world ML applications.</li><li><strong>Peter Skomoroch</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/peteskomoroch"><strong>linkedin.com/in/peteskomoroch</strong></a>): A veteran in building data-driven products, his profile is a must-follow for those interested in creating impactful ML solutions at scale.</li></ul><p><strong>Why follow them?</strong> Learn the nuts and bolts of machine learning engineering and gain practical knowledge you can apply directly to your projects.</p><h3>The Strategists and Communicators: Making AI Understandable</h3><p>These leaders bridge the gap between complex tech and real-world impact:</p><ul><li><strong>Allie K. Miller</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/alliekmiller"><strong>linkedin.com/in/alliekmiller</strong></a>): The go-to voice for AI in business. Follow her for insights on AI startups, investment trends, and how businesses are leveraging AI for growth.</li><li><strong>Bernard Marr</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/bernardmarr"><strong>linkedin.com/in/bernardmarr</strong></a>): A futurist who helps organizations understand and implement AI strategies for better performance.</li><li><strong>Cassie Kozyrkov</strong>: The pioneer of “Decision Intelligence.” Her posts focus on making data-driven decisions and understanding the practical application of AI in business.</li><li><strong>Kirk Borne</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/kirkdborne"><strong>linkedin.com/in/kirkdborne</strong></a>): A data science veteran who shares valuable articles and insights across the data science and AI landscape.</li></ul><p><strong>Why follow them?</strong> Understand the business implications of AI and learn how to effectively communicate its value.</p><h3>The Community and Ethics Advocates: Shaping a Responsible Future</h3><p>These influencers focus on making AI accessible and ethical:</p><ul><li><strong>Steve Nouri</strong>: A strong advocate for diversity and ethics in AI. Follow him for discussions on building responsible and inclusive AI systems.</li><li><strong>Sebastian Raschka</strong>: A leading AI educator who shares tutorials and practical insights into machine learning and deep learning.</li><li><strong>Kate Strachnyi</strong> (<a href="https://www.google.com/search?q=https://linkedin.com/in/katestrachnyi"><strong>linkedin.com/in/katestrachnyi</strong></a>): Focuses on data visualization, storytelling, and building a strong data science community.</li></ul><p><strong>Why follow them?</strong> Learn about the crucial ethical considerations in AI and connect with a supportive community of learners.</p><h3>Building Your Path</h3><p>“Following the path” in machine learning isn’t about blindly copying someone’s journey. It’s about learning from diverse experts, understanding different facets of the field, and carving your own niche. Use this list as a starting point to curate your LinkedIn feed and actively engage with their content. Ask questions, participate in discussions, and most importantly, keep learning! Your AI journey starts now.</p><p><strong>Ready to level up? Start connecting with these influential figures today!</strong></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4e15a7c99f00" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>