<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Vibe Coding Forem: Darian Vance</title>
    <description>The latest articles on Vibe Coding Forem by Darian Vance (@techresolve).</description>
    <link>https://vibe.forem.com/techresolve</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3680308%2F11dcc006-d168-4824-88ae-6faa9b9bb9ee.jpg</url>
      <title>Vibe Coding Forem: Darian Vance</title>
      <link>https://vibe.forem.com/techresolve</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://vibe.forem.com/feed/techresolve"/>
    <language>en</language>
    <item>
      <title>Solved: Launched: StackSage – AWS cost reports for SMEs (privacy-first, read-only)</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Mon, 09 Mar 2026 07:16:34 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-launched-stacksage-aws-cost-reports-for-smes-privacy-first-read-only-2hob</link>
      <guid>https://vibe.forem.com/techresolve/solved-launched-stacksage-aws-cost-reports-for-smes-privacy-first-read-only-2hob</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AWS cost overruns stem from frictionless resource creation and poor visibility. This guide outlines three strategies: immediate alerts via AWS Budgets, proactive cost attribution through mandatory tagging, and preventative architectural controls using Service Control Policies (SCPs).&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Implement AWS Budgets with AWS Chatbot integration for real-time alerts on actual and forecasted spend, acting as a ‘tripwire’ for unexpected cost spikes.&lt;/li&gt;
&lt;li&gt;Enforce a strict, well-defined tagging policy (e.g., owner, project, environment, termination_date) across all AWS resources to enable granular cost visibility and attribution using tools like AWS Cost Explorer or StackSage.&lt;/li&gt;
&lt;li&gt;Utilize Service Control Policies (SCPs) within AWS Organizations to prevent the provisioning of notoriously expensive instance types (e.g., p4d.*, p5.*) in non-production accounts, acting as a ‘nuclear option’ for cost control.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stop dreading your AWS bill. A senior engineer breaks down the real reasons for cloud cost overruns and provides three actionable strategies—from immediate alerts to long-term architectural controls.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wrestling the AWS Cost Monster: 3 Fixes Before You Go Broke
&lt;/h1&gt;

&lt;p&gt;I’ll never forget the Monday morning I saw the Slack alert. A junior engineer, full of weekend enthusiasm, had spun up a fleet of &lt;code&gt;p4d.24xlarge&lt;/code&gt; instances for an ML experiment… and forgotten to turn them off. The projected bill was more than my first car. That’s the day AWS cost management stopped being an abstract concept and became a very, very real problem for me. We’ve all been there, staring at a Cost Explorer graph that looks more like a rocket launch than a budget.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Your AWS Bill Is a Ticking Time Bomb
&lt;/h2&gt;

&lt;p&gt;Listen, the problem isn’t usually a single, massive mistake. It’s death by a thousand paper cuts. AWS is designed for frictionless provisioning. That’s its superpower and its curse. A developer needs a database for a proof-of-concept? Click, click, boom: a managed RDS instance is running. A data scientist wants to test a new model? Spin up a SageMaker notebook. The root cause is a combination of two things: &lt;strong&gt;frictionless creation&lt;/strong&gt; and &lt;strong&gt;high-friction visibility&lt;/strong&gt;. It’s too easy to create resources and too hard to track who owns them, why they exist, and how much they’re costing you until it’s too late.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1: The Quick Fix – Set Up the Tripwire
&lt;/h2&gt;

&lt;p&gt;Before you do anything else, you need a smoke alarm. This isn’t a permanent solution, but it will save you from a five-figure surprise. The tool for this is &lt;strong&gt;AWS Budgets&lt;/strong&gt;. It’s simple, it’s native, and it takes ten minutes to configure.&lt;/p&gt;

&lt;p&gt;Your goal is to set a budget slightly higher than your normal monthly spend and have it scream at you via email and Slack when you’re about to cross a threshold. You’re not stopping the spend, you’re just making yourself aware of it &lt;em&gt;before&lt;/em&gt; the billing cycle ends.&lt;/p&gt;

&lt;p&gt;Here’s a basic setup. Go to AWS Budgets, create a new cost budget, and configure an alert to trigger when your &lt;strong&gt;actual&lt;/strong&gt; spend hits 80% of the budgeted amount, and another when your &lt;strong&gt;forecasted&lt;/strong&gt; spend is projected to hit 110%.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; Don’t just send the alerts to a generic “devops” email list that everyone ignores. Pipe them directly into your team’s main Slack channel using the AWS Chatbot integration. Public visibility creates accountability.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solution 2: The Permanent Fix – Mandate Visibility with Tagging
&lt;/h2&gt;

&lt;p&gt;Alerts are reactive. To be proactive, you need to understand &lt;em&gt;what&lt;/em&gt; is costing you money. The only way to do that at scale is with a non-negotiable, enforced tagging policy. Tags are the metadata that turns your chaotic list of resources into a queryable inventory.&lt;/p&gt;

&lt;p&gt;This is where tools like the one I saw on Reddit, StackSage, come into play. They provide a read-only, privacy-first way to slice and dice your costs without needing to give a third party god-mode access to your account. But a tool is only as good as the data it has. Your tagging policy is that data.&lt;/p&gt;

&lt;p&gt;Here’s what a decent policy looks like compared to a useless one:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tag Key&lt;/th&gt;
&lt;th&gt;Bad Example&lt;/th&gt;
&lt;th&gt;Good Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;owner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;dave&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dave.smith@techresolve.com&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;project&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;database&lt;/td&gt;
&lt;td&gt;&lt;code&gt;project-phoenix-billing&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;environment&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;prod&lt;/td&gt;
&lt;td&gt;&lt;code&gt;production&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;termination_date&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;(missing)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2024-12-31&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Once you have this, you can use AWS Cost Explorer’s filtering or a dedicated tool to finally answer questions like, “How much is Project Phoenix costing us in staging environments?” or “Show me all resources owned by engineers who have left the company.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3: The ‘Nuclear’ Option – Architect for Frugality
&lt;/h2&gt;

&lt;p&gt;Sometimes, you need to stop bad behavior before it can even start. This is the “you must be this tall to ride” approach, and it’s implemented using Service Control Policies (SCPs) within AWS Organizations. This is for when you’re tired of playing whack-a-mole with oversized instances.&lt;/p&gt;

&lt;p&gt;An SCP is a guardrail that applies to entire accounts within your organization. It lets you define what actions are explicitly denied. For example, you can completely block the ability to launch notoriously expensive instance families in developer sandbox accounts.&lt;/p&gt;

&lt;p&gt;Here’s a simple SCP that prevents any IAM user or role in an affected account from launching specific, high-cost EC2 instance types. They won’t even show up as an option for your junior dev to “accidentally” click.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyExpensiveInstanceTypes",
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringLike": {
          "ec2:InstanceType": [
            "*.16xlarge",
            "*.24xlarge",
            "p4d.*",
            "p5.*",
            "inf2.*"
          ]
        }
      }
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Be careful with SCPs. They are a blunt instrument. You can easily break legitimate production workloads if you apply them to the wrong OU. Test them in a sandbox organization unit first. This is a powerful tool, not a toy.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ultimately, cloud cost management isn’t a single project; it’s a cultural shift. Start with the alerts, build a culture of visibility with tags, and when you’re ready, enforce the rules with architectural guardrails. Your CFO will thank you.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nE" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Botched Domain Migration in Jan 2024 – Just Discovered the Damage. How Do I Fix This?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Mon, 09 Mar 2026 07:14:31 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-botched-domain-migration-in-jan-2024-just-discovered-the-damage-how-do-i-fix-this-4jjm</link>
      <guid>https://vibe.forem.com/techresolve/solved-botched-domain-migration-in-jan-2024-just-discovered-the-damage-how-do-i-fix-this-4jjm</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; A botched domain migration often leads to ‘Access Denied’ errors because applications store user permissions by old, orphaned Active Directory Security Identifiers (SIDs). The fix involves diagnosing new SIDs and updating application databases, with Active Directory Migration Tool (ADMT) and sIDHistory migration being the crucial preventative measure.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Domain migrations can cripple applications by creating ‘orphaned’ user accounts, where application databases still reference old Active Directory Security Identifiers (SIDs) instead of new ones.&lt;/li&gt;
&lt;li&gt;The Active Directory Migration Tool (ADMT) is critical for domain migrations, specifically its feature to migrate &lt;code&gt;sIDHistory&lt;/code&gt;, which stamps a user’s old SID onto their new account, ensuring applications recognize them.&lt;/li&gt;
&lt;li&gt;Solutions for SID mismatch issues range from emergency manual SQL updates for critical users, to scalable PowerShell scripts for bulk database remediation, or a ‘Nuke and Pave’ strategy involving database restoration and re-migration as a last resort.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A botched domain migration can leave your applications crippled by “orphaned” user accounts linked to old SIDs. This guide provides a senior engineer’s playbook for diagnosing the SID mismatch issue and implementing realistic fixes, from emergency SQL patches to permanent, scripted solutions.&lt;/p&gt;

&lt;h1&gt;
  
  
  We Botched a Domain Migration. Here’s How to Fix the Lingering Damage.
&lt;/h1&gt;

&lt;p&gt;I still remember the knot in my stomach. It was a Tuesday morning, and the Director of Sales was on the phone, his voice a little too calm. “Darian, none of my regional managers can access the Q1 forecast dashboard. It just says ‘Access Denied’. This was working on Friday.” My coffee suddenly tasted like battery acid. We had just finished a “seamless” domain migration over the weekend. Turns out, the LOB application that ran the dashboard stored user permissions by their Active Directory Security Identifier (SID), and we had just created thousands of new SIDs for everyone, leaving the old ones orphaned in the application’s database. That was a long, long day of manual SQL updates and a career-defining “learning experience.”&lt;/p&gt;

&lt;p&gt;Seeing a recent Reddit thread about this exact problem from January brought it all rushing back. Someone’s team migrated from &lt;code&gt;CORP.LOCAL&lt;/code&gt; to a new domain, and months later, they’re discovering the fallout. If you’re in that boat, take a deep breath. You’re not the first, and you won’t be the last. Let’s get this sorted.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, Let’s Understand the “Why”
&lt;/h2&gt;

&lt;p&gt;This isn’t just about a username changing. In the world of Windows and Active Directory, your identity isn’t just &lt;code&gt;DOMAIN\username&lt;/code&gt;. The real key to the kingdom is your &lt;strong&gt;Security Identifier (SID)&lt;/strong&gt;. It’s a unique, non-reusable string of characters that AD assigns to a user, group, or computer.&lt;/p&gt;

&lt;p&gt;When you migrated from the old domain to the new one, every user effectively got a brand-new SID. Your application, however, still has the &lt;em&gt;old&lt;/em&gt; SID stored in its user permissions table. So, when &lt;code&gt;NEWDOMAIN\jsmith&lt;/code&gt; tries to log in, the application looks up their permissions, finds nothing for their new SID, and promptly tells them to get lost. The user exists, but their link to their permissions is broken.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; This is why tools like the Active Directory Migration Tool (ADMT) are critical. They have features to migrate &lt;code&gt;sIDHistory&lt;/code&gt;, which stamps the user’s old SID onto their new account. This allows applications to recognize the user by either their new SID or their old one, providing a much smoother transition.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Recovery Playbook: Three Tiers of Fixes
&lt;/h2&gt;

&lt;p&gt;Depending on the scale of the damage and the time you have, there are a few ways to approach this. We’ll go from the quick band-aid to the proper surgical fix.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Triage: A Quick and Dirty SQL Fix
&lt;/h3&gt;

&lt;p&gt;This is your emergency, “get the CEO back online” solution. It’s manual, it doesn’t scale, but it works in a pinch. The goal is to find the old, orphaned SID in your database and manually replace it with the new, correct one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1: Get the user’s NEW SID.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You can do this with a quick PowerShell command on a domain controller:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Get-AdUser -Identity 'jsmith' | Select-Object SID
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s say this returns &lt;code&gt;S-1-5-21-1234567890-123456789-1234567890-5512&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2: Find the user’s OLD SID in the database.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
You’ll need to know your application’s schema, but you’re probably looking for a &lt;code&gt;Users&lt;/code&gt; or &lt;code&gt;Permissions&lt;/code&gt; table. You’ll have to find the record by username.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT UserSID, UserName, UserId
FROM dbo.ApplicationUsers
WHERE UserName = 'CORP\jsmith';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This might return the old, orphaned SID: &lt;code&gt;S-1-5-21-9876543210-987654321-9876543210-4891&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3: Update the record.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Now, perform a targeted &lt;code&gt;UPDATE&lt;/code&gt;. &lt;strong&gt;Always wrap this in a transaction!&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BEGIN TRANSACTION;

UPDATE dbo.ApplicationUsers
SET UserSID = 'S-1-5-21-1234567890-123456789-1234567890-5512' -- The NEW SID
WHERE UserName = 'CORP\jsmith' 
AND UserSID = 'S-1-5-21-9876543210-987654321-9876543210-4891'; -- The OLD SID

-- COMMIT; 
-- ROLLBACK; -- Always have your escape hatch ready.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fast for one or two users, but a nightmare for hundreds.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Scalpel: A Permanent, Scripted Solution
&lt;/h3&gt;

&lt;p&gt;This is the proper engineering solution. We’re going to write a script to do the heavy lifting. The logic is simple: for each user in our database, we query the new Active Directory for their new SID and update the database record. This is perfect for cleaning up the entire user base in one controlled, repeatable process.&lt;/p&gt;

&lt;p&gt;Here’s a conceptual PowerShell script to show the logic. This assumes you’re running it from a machine that can talk to both your database and your new domain controllers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# --- DISCLAIMER: THIS IS A CONCEPTUAL SCRIPT. TEST THOROUGHLY! ---

# Import necessary modules
Import-Module ActiveDirectory
Import-Module SqlServer

# Database connection details
$sqlInstance = "prod-db-01"
$database = "AppDatabase"

# Get all users from the app database with old domain prefix
$query = "SELECT UserId, UserName, UserSID FROM dbo.ApplicationUsers WHERE UserName LIKE 'CORP\%'"
$appUsers = Invoke-Sqlcmd -ServerInstance $sqlInstance -Database $database -Query $query

# Loop through each user and fix them
foreach ($user in $appUsers) {
    # Extract the username (e.g., 'jsmith' from 'CORP\jsmith')
    $samAccountName = ($user.UserName -split '\\')[1]

    try {
        # Find the user in the NEW domain
        $adUser = Get-ADUser -Identity $samAccountName -ErrorAction Stop
        $newSID = $adUser.SID.Value

        # If the SIDs don't match, update the database
        if ($newSID -ne $user.UserSID) {
            Write-Host "Fixing user: $($user.UserName)... OLD SID: $($user.UserSID), NEW SID: $newSID"

            # Construct and run the update query
            $updateQuery = "UPDATE dbo.ApplicationUsers SET UserSID = '$newSID' WHERE UserId = $($user.UserId)"
            # Invoke-Sqlcmd -ServerInstance $sqlInstance -Database $database -Query $updateQuery
            Write-Host "--&amp;gt; (Simulated) Update for $samAccountName complete."
        }
    }
    catch {
        Write-Warning "Could not find user '$samAccountName' in the new domain. Skipping."
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CRITICAL WARNING:&lt;/strong&gt; Test this script against a restored copy of your production database first. A tiny logic error in a script like this can cause catastrophic, resume-generating damage. Test, test, and test again before uncommenting that &lt;code&gt;Invoke-Sqlcmd&lt;/code&gt; line.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. The ‘Nuke and Pave’: When All Else Fails
&lt;/h3&gt;

&lt;p&gt;Sometimes the damage is too widespread, the database schema is too complex, or you have zero confidence in your data’s integrity. This is the last resort.&lt;/p&gt;

&lt;p&gt;The “Nuke and Pave” involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Restore:&lt;/strong&gt; Restore the application database from the last known-good backup taken *before* the domain migration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Halt:&lt;/strong&gt; Take the application offline to prevent any new data from being written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-Migrate Correctly:&lt;/strong&gt; Use a tool like ADMT to re-migrate the users, but this time, ensure you are migrating the &lt;code&gt;sIDHistory&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-Launch:&lt;/strong&gt; Once the identity foundation is correct (users have their old SID in their history), bring the application back online. Users from the new domain should now be recognized by the application because it can resolve their identity via their SID History.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This option means data loss (anything entered between the migration and the restore is gone) and significant downtime. It’s a painful, high-visibility option, but sometimes it’s the only way to be 100% sure you have a clean slate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparing the Solutions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Speed&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;th&gt;Scalability&lt;/th&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. The Triage (SQL Fix)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very Fast (per user)&lt;/td&gt;
&lt;td&gt;Low (if careful)&lt;/td&gt;
&lt;td&gt;Very Poor&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. The Scalpel (Scripted)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast (for all users)&lt;/td&gt;
&lt;td&gt;High (if not tested)&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. The Nuke and Pave&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very Slow (days)&lt;/td&gt;
&lt;td&gt;Extreme (data loss)&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;Very High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;My advice? Start with the Triage for your most critical users to stop the bleeding. While they’re back to work, you can develop and, more importantly, &lt;em&gt;test&lt;/em&gt; the Scalpel solution to fix everyone else. The Nuclear Option should only be on the table if you suspect this SID issue is just the tip of a much larger iceberg of migration-related data corruption.&lt;/p&gt;

&lt;p&gt;Good luck. And next time, let’s get that &lt;code&gt;sIDHistory&lt;/code&gt; migration checked off the list *before* go-live.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nD" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: How do you prevent FE regressions?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 20:57:43 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-how-do-you-prevent-fe-regressions-1c7k</link>
      <guid>https://vibe.forem.com/techresolve/solved-how-do-you-prevent-fe-regressions-1c7k</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Frontend regressions often stem from aggressive browser caching of unversioned assets, causing users to see outdated content. The most effective solution involves automated asset hashing during the build process, coupled with strategic server-side caching headers to ensure ‘index.html’ is always fresh while hashed assets are cached indefinitely for performance.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Browser caching of assets with identical filenames, despite content changes, is the primary cause of frontend regressions.&lt;/li&gt;
&lt;li&gt;Automated asset hashing (e.g., ‘main.a8b4f9c1.js’) via build tools like Webpack or Vite is the standard, reliable method to guarantee browsers download new versions.&lt;/li&gt;
&lt;li&gt;A robust caching strategy requires server-side configuration (e.g., Nginx) to aggressively cache hashed assets while explicitly preventing caching of the ‘index.html’ entry point.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prevent painful frontend regressions caused by aggressive browser caching. A senior DevOps engineer shares battle-tested strategies from quick manual fixes to permanent, automated architectural solutions.&lt;/p&gt;

&lt;h1&gt;
  
  
  So, You Broke Prod with a CSS Change Again? Let’s Talk Caching.
&lt;/h1&gt;

&lt;p&gt;I remember it like it was yesterday. It was a 2 AM deployment for a massive e-commerce launch. Everything looked perfect in staging. We pushed the button. Minutes later, Slack explodes. Half our users are seeing a completely broken checkout page—buttons misaligned, text overlapping. The other half? Perfectly fine. The dev who pushed the “simple CSS fix” was frantically trying to revert, but nothing was changing for the affected users. It was chaos. The culprit? A single line in our Nginx config, telling browsers to cache our &lt;code&gt;main.css&lt;/code&gt; file for 24 hours. We had served our users a broken file, and their own browsers were now refusing to let it go.&lt;/p&gt;

&lt;h2&gt;
  
  
  The “Why”: Your Browser is a Hoarder
&lt;/h2&gt;

&lt;p&gt;Let’s be clear: caching is a good thing. It makes websites fast. When a user visits your site, their browser downloads assets like CSS and JavaScript files. To save time on the next visit, it stores them locally. The problem isn’t the caching; it’s the &lt;strong&gt;naming&lt;/strong&gt;. When you deploy a new version of &lt;code&gt;app.js&lt;/code&gt;, but the filename is still &lt;code&gt;app.js&lt;/code&gt;, the browser has no idea it has changed. It looks at its local cache and says, “Hey, I’ve already got a file called &lt;code&gt;app.js&lt;/code&gt;. I’ll just use that.” And boom, your user is running old code, causing what we call a “frontend regression.”&lt;/p&gt;

&lt;p&gt;The root of this is how we, as engineers, signal to the browser that a file is “new.” If the name doesn’t change, the browser assumes the content hasn’t either.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fixes: From Duct Tape to a New Engine
&lt;/h2&gt;

&lt;p&gt;I’ve seen teams handle this in a few ways, ranging from “panic mode” fixes to long-term, robust solutions. Let’s break them down.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Quick Fix: The “Midnight Hotfix” Query String
&lt;/h3&gt;

&lt;p&gt;This is the fastest, dirtiest way to force a browser to re-download a file. You manually append a query string to your asset link in your &lt;code&gt;index.html&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So, this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="stylesheet" href="/css/styles.css"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Becomes this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;link rel="stylesheet" href="/css/styles.css?v=1.0.1"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most browsers treat a URL with a different query string as a completely new file, forcing a re-download. It’s manual, error-prone (someone WILL forget to update the version number), and not a real strategy. But if prod is on fire at 2 AM and you need to force-invalidate a file for your users &lt;em&gt;right now&lt;/em&gt;, this will get you out of a jam.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Permanent Fix: Automated Asset Hashing
&lt;/h3&gt;

&lt;p&gt;This is how modern, professional frontends are built. Instead of you manually versioning things, your build tool (like Webpack, Vite, or Parcel) does it for you. It looks at the contents of a file and generates a unique “hash” that it appends to the filename.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;main.js&lt;/code&gt; might become &lt;code&gt;main.a8b4f9c1.js&lt;/code&gt;. If you change even one character in that file and rebuild, the new name might be &lt;code&gt;main.3e9d8f2a.js&lt;/code&gt;. Because the filename itself changes on every build, the browser is &lt;em&gt;guaranteed&lt;/em&gt; to download the new version. The old file can be cached forever—it doesn’t matter, because it will never be referenced again.&lt;/p&gt;

&lt;p&gt;Your build process will automatically update your &lt;code&gt;index.html&lt;/code&gt; to point to the new, hashed files. You set it up once, and it just works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; When using hashed assets, you can configure your web server or CDN to cache them very aggressively—even for a year! Since the name will change if the content does, there’s no risk of serving stale assets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3. The ‘Nuclear’ Option: The Server-Side Hammer
&lt;/h3&gt;

&lt;p&gt;Even with hashed assets, you can have one final point of failure: the &lt;code&gt;index.html&lt;/code&gt; file itself. What if a user’s browser caches that file? They’ll get an old &lt;code&gt;index.html&lt;/code&gt; that points to old, hashed JS and CSS files. Ouch.&lt;/p&gt;

&lt;p&gt;This is where we bring out the server-side hammer. We configure our web server (like Nginx) or CDN (like CloudFront) with specific rules for different file types. The strategy is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For your hashed assets (e.g., &lt;code&gt;\*.a8b4f9c1.js&lt;/code&gt;), set aggressive caching headers.&lt;/li&gt;
&lt;li&gt;For your main entrypoint (&lt;code&gt;index.html&lt;/code&gt;), explicitly tell the browser &lt;strong&gt;not&lt;/strong&gt; to cache it, or to always revalidate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s a simplified Nginx config example to illustrate the point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;server {
    listen 80;
    server_name my-app.techresolve.com;
    root /var/www/html;
    index index.html;

    # Rule for our main entrypoint - DO NOT CACHE.
    location = /index.html {
        add_header Cache-Control 'no-cache, no-store, must-revalidate';
        add_header Pragma 'no-cache';
        add_header Expires '0';
    }

    # Rule for our hashed, static assets - CACHE FOREVER.
    location ~* \.(?:css|js)$ {
        # Check if the filename contains a hash-like pattern (e.g., 8 hex chars)
        if ($uri ~* "\.[a-f0-9]{8}\.(css|js)$") {
            add_header Cache-Control 'public, max-age=31536000, immutable';
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This config tells browsers: “Never trust your local copy of &lt;code&gt;index.html&lt;/code&gt;, always ask my server for the latest. But for any CSS or JS file with a hash in its name? Keep it for a year, don’t even bother asking me again.” This combination gives you the best of both worlds: blazing-fast performance for assets and instant updates for your application logic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Your Weapon
&lt;/h2&gt;

&lt;p&gt;So, how do you decide which to use? Here’s how I think about it:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Effort&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Reliability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;When to Use It&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query String&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Emergency hotfix when everything else has failed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Asset Hashing&lt;/td&gt;
&lt;td&gt;Medium (Initial Setup)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;High&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The default, standard practice for any modern web application.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Server-Side Headers&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Very High&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;In conjunction with Asset Hashing to create a bulletproof deployment strategy.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Stop fighting fires. Investing a little time in setting up a proper asset hashing and caching strategy in your build pipeline and server config will save you countless hours of stress and prevent you from ever having to explain to a product manager why their “new feature” isn’t showing up for half the user base. Trust me, your future self will thank you.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nC" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Anyone else tired of paying for 6 different apps just to run basic store operations?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 20:55:13 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-anyone-else-tired-of-paying-for-6-different-apps-just-to-run-basic-store-operations-25md</link>
      <guid>https://vibe.forem.com/techresolve/solved-anyone-else-tired-of-paying-for-6-different-apps-just-to-run-basic-store-operations-25md</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; E-commerce businesses often suffer from ‘app sprawl’ where multiple disconnected applications lead to integration failures and operational complexity. This article proposes three solutions: implementing ‘glue code’ with serverless functions for quick fixes, architecting a ‘centralized event bus’ for scalable decoupling, or undertaking a ‘nuclear option’ to re-evaluate and consolidate the entire tech stack.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;“App sprawl” results from the “best-of-breed” approach, creating unreliable point-to-point integrations and multiple points of failure.&lt;/li&gt;
&lt;li&gt;A “centralized event bus” (e.g., AWS SNS/SQS, Google Pub/Sub, Kafka) implements a publish-subscribe model to decouple services, allowing applications to react to events without direct integration.&lt;/li&gt;
&lt;li&gt;Re-evaluating the entire stack, potentially consolidating into an all-in-one platform or even a monolith, can reduce operational overhead and integration headaches for small-to-medium businesses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tired of paying for multiple apps that don’t talk to each other? A Senior DevOps Engineer breaks down why this ‘app sprawl’ happens and offers three practical solutions, from quick scripting fixes to long-term architectural sanity.&lt;/p&gt;

&lt;h1&gt;
  
  
  Stop the Bleeding: Why Your E-Commerce Stack is a 6-App Nightmare (And How to Fix It)
&lt;/h1&gt;

&lt;p&gt;I remember a 2 AM PagerDuty alert like it was yesterday. The site was up, the databases were fine, but our sales team was panicking because a flash sale had just kicked off and our inventory system wasn’t syncing with our storefront. Turns out, the third-party connector app we paid $150 a month for decided to silently fail its authentication token refresh. We were overselling products we didn’t have. That night, debugging a glorified webhook between two SaaS platforms, I thought to myself, “We’re paying thousands for this complexity. There has to be a better way.”&lt;/p&gt;

&lt;h2&gt;
  
  
  The “Why”: How We Got Into This Mess
&lt;/h2&gt;

&lt;p&gt;Listen, this problem isn’t accidental. It’s the direct result of the “API-first” and “best-of-breed” revolution. We were sold a dream: pick the absolute best tool for every single job. The best email marketing app, the best customer support desk, the best inventory management, the best shipping logistics. The pitch is that they’ll all just “talk to each other” seamlessly via APIs. In reality, you’ve just become the unpaid, stressed-out system integrator for half a dozen different companies whose only shared goal is getting your credit card number each month. Each connection is a new point of failure, a new security risk, and another subscription to manage.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; Don’t mistake “has an API” for “has a good, reliable, and well-maintained integration.” The devil is always in the details, like rate limits, authentication schemes, and data consistency models.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, how do we climb out of this hole? It depends on how much time and runway you have. I see three main paths forward.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fixes: From Band-Aids to Brain Surgery
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. The Quick Fix: The ‘Glue Code’ Band-Aid
&lt;/h3&gt;

&lt;p&gt;This is the “I need this working by morning” solution. You identify the most critical, unreliable connection and you take control of it yourself. You write a small, focused piece of code—what we lovingly call ‘glue code’—that does one job and does it well. The best tool for this is a serverless function (like AWS Lambda, Google Cloud Functions, or Azure Functions) on a timer.&lt;/p&gt;

&lt;p&gt;Let’s say your inventory app (App A) needs to update your e-commerce platform (App B). Instead of relying on a flaky third-party connector, you write a simple script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example: A Python Lambda function to sync stock levels.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import requests
import os

# Environment variables are your friend!
APP_A_API_KEY = os.environ.get('APP_A_API_KEY')
APP_B_API_KEY = os.environ.get('APP_B_API_KEY')

def sync_inventory(event, context):
    # 1. Fetch data from the source of truth
    headers_a = {'Authorization': f'Bearer {APP_A_API_KEY}'}
    inventory_data = requests.get('https://api.appa.com/v2/products/stock', headers=headers_a).json()

    # 2. Transform the data if necessary (they never use the same format)
    transformed_payload = []
    for item in inventory_data['items']:
        transformed_payload.append({
            'sku': item['product_sku'],
            'quantity': item['stock_on_hand']
        })

    # 3. Push the data to the destination
    headers_b = {'X-Api-Key': APP_B_API_KEY}
    response = requests.post('https://api.appb.com/v1/inventory/bulk_update', headers=headers_b, json=transformed_payload)

    if response.status_code != 200:
        # Basic error handling - send an alert to Slack or CloudWatch!
        print("ERROR: Sync failed!")

    return {'status': 'success'}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You set this up on a cron job (e.g., run every 5 minutes via Amazon EventBridge) and now &lt;strong&gt;you&lt;/strong&gt; own the logic. It’s not glamorous, and you can end up with a lot of these little functions, but it’s cheap, reliable, and puts you back in control when things go wrong.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Permanent Fix: The Centralized Event Bus
&lt;/h3&gt;

&lt;p&gt;If you’re tired of playing whack-a-mole with glue code, it’s time to think like an architect. The problem with point-to-point integrations is that they create a tangled spiderweb. The solution is to create a central “nervous system” for your business operations—an event bus.&lt;/p&gt;

&lt;p&gt;Instead of App A talking directly to App B, App A just shouts into the void, “Hey, an order was just placed!” or “Hey, inventory for sku-123 is now 50!”. This “shout” is an event that gets published to a central message queue like AWS SNS/SQS, Google Pub/Sub, or a managed Kafka stream. Then, any other application that cares about that event can subscribe and react accordingly. Your e-commerce platform, your shipping software, and your analytics database all listen for the “Order Placed” event and do their own thing with the data.&lt;/p&gt;

&lt;p&gt;This is called a “publish-subscribe” or “pub/sub” model. It decouples your services completely.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Before: The Spaghetti Mess&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;After: The Event Bus (Hub &amp;amp; Spoke)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;* Shopify talks to ShipStation * Shopify talks to Klaviyo * ShipStation talks to Shopify * QuickBooks talks to Shopify&lt;/td&gt;
&lt;td&gt;* Shopify &lt;strong&gt;publishes&lt;/strong&gt; ‘NEW_ORDER’ event * ShipStation &lt;strong&gt;subscribes&lt;/strong&gt; to ‘NEW_ORDER’ * Klaviyo &lt;strong&gt;subscribes&lt;/strong&gt; to ‘NEW_ORDER’ * QuickBooks &lt;strong&gt;subscribes&lt;/strong&gt; to ‘NEW_ORDER’&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The beauty here is that if you want to add a new CRM system next year, you don’t have to touch any of your existing systems. You just create a new subscriber that listens for the events it cares about. It’s more work to set up initially, but it’s how you scale without losing your mind.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The ‘Nuclear’ Option: Re-evaluate Your Stack Entirely
&lt;/h3&gt;

&lt;p&gt;I’m going to say something controversial for a cloud architect: sometimes, the best microservice architecture is a monolith. For many small-to-medium businesses, the operational overhead of managing six different best-of-breed apps is simply not worth the marginal benefit over an 80% “good enough” all-in-one platform.&lt;/p&gt;

&lt;p&gt;This is the “rip it all out” option. You sit down and ask the hard questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do we &lt;em&gt;really&lt;/em&gt; need this standalone support desk, or can we get by with the features in our primary e-commerce platform?&lt;/li&gt;
&lt;li&gt;Is the 5% conversion lift from this fancy email marketing tool worth the integration headaches and the $500/month subscription?&lt;/li&gt;
&lt;li&gt;Could we consolidate three of these apps into one higher-tier plan from a single vendor, like a Shopify Plus or a BigCommerce Enterprise?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moving platforms is a painful, expensive process. But sometimes, the cost of that migration is less than the slow, continuous financial and sanity drain of maintaining a Frankenstein’s monster of a tech stack. It’s a business decision, not just a technical one, but it’s one that engineering needs to have a strong voice in.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nB" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: What are the best converting affiliate products to sell from Digistore24 or Clickbank?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 20:27:18 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-what-are-the-best-converting-affiliate-products-to-sell-from-digistore24-or-clickbank-16f5</link>
      <guid>https://vibe.forem.com/techresolve/solved-what-are-the-best-converting-affiliate-products-to-sell-from-digistore24-or-clickbank-16f5</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; DevOps engineers frequently face misdirected requests from marketing regarding business metrics like affiliate product conversion rates, causing productivity loss due to organizational disconnect. The solution involves implementing strategies from polite redirection and establishing clear process boundaries to a ‘malicious compliance’ approach, effectively managing these requests and refocusing on core engineering tasks.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Misdirected requests for business logic or sales data to DevOps signify a ‘wrong API’ call, necessitating a ‘404 Not Found’ response and redirection to appropriate teams like Business Intelligence or Affiliate Management.&lt;/li&gt;
&lt;li&gt;Establishing clear ‘lanes of responsibility’ through centralized ticketing systems (e.g., Jira) or shared documentation (e.g., Confluence tables) is crucial for correctly routing requests and protecting engineering focus.&lt;/li&gt;
&lt;li&gt;For persistent misdirection, a ‘malicious compliance’ strategy involves scoping out a full-blown engineering project, detailing infrastructure costs (e.g., AWS Kinesis, Redshift, Grafana) to demonstrate the technical complexity and cost of the misdirected request, thereby educating the requester.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stuck fielding requests from marketing about ‘high-converting products’? Learn why this isn’t your job, and get three strategies—from a quick redirect to a permanent fix—to get back to real engineering.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Wrong API: When Marketing Asks a DevOps Engineer About ClickBank Conversion Rates
&lt;/h1&gt;

&lt;p&gt;It was 2 PM on a Tuesday. I was knee-deep in a Terraform state lock issue for our &lt;code&gt;prod-k8s-cluster-us-east-1&lt;/code&gt; when a Slack message popped up from someone in Marketing. “Hey Darian, quick question. We’re looking at Digistore24 and Clickbank. What are the best converting affiliate products to sell?” I stared at the message for a solid minute. I build and maintain the systems that run the business; I don’t have a magic crystal ball that tells me which diet supplement VSL is crushing it this week. This isn’t a one-off problem; it’s a symptom of a larger organizational disconnect, and it kills productivity for engineers everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Why Does This Even Happen?
&lt;/h2&gt;

&lt;p&gt;Let’s be empathetic for a second. To a lot of non-technical teams, “Tech” is a monolithic black box. If it involves a computer, a server, or data, it must be our domain. They see us wrangling complex cloud infrastructure and assume our expertise extends to the actual business logic and sales data flowing through it. They’ve connected “data” with “DevOps,” and their mental model has drawn a straight line from their problem to us. It’s not malice; it’s a failure to define boundaries and communicate what we actually &lt;em&gt;do&lt;/em&gt;. They’re calling the wrong API endpoint, and our job is to return a helpful, yet firm, &lt;code&gt;404 Not Found&lt;/code&gt; and point them to the right documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Quick Fix: The Polite Redirect
&lt;/h3&gt;

&lt;p&gt;Your first instinct might be frustration, but your first action should be redirection. Your goal is to get this off your plate immediately without burning bridges. You are not the keeper of this information, and the fastest way to solve their problem (and yours) is to connect them with who is.&lt;/p&gt;

&lt;p&gt;Here’s a template you can steal for Slack or email:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hey [Marketer's Name],

That's an interesting question! My focus is on the cloud infrastructure, CI/CD pipelines, and platform reliability, so I honestly have no insight into affiliate product performance. 

That sounds like a question for the Business Intelligence team or maybe [Affiliate Manager's Name]. They're the ones who live in that data and can probably give you a detailed breakdown.

I'm going to bow out so you can connect with the right experts!

Best,
Darian
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Do not, under any circumstances, try to guess or give an opinion. The second you say, “Well, I heard the ‘Keto Diet’ space is popular,” you’ve just signed up to be their new, unofficial marketing consultant. Be helpful, but stay in your lane.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Permanent Fix: Defining Boundaries with Process
&lt;/h3&gt;

&lt;p&gt;One-off redirects are fine, but if this happens constantly, you have a process problem, not a people problem. The long-term solution is to work with your manager and other team leads to establish clear “lanes of responsibility.” In DevOps, we use tools to manage workflows; there’s no reason the rest of the business can’t benefit from that clarity.&lt;/p&gt;

&lt;p&gt;Push for a centralized ticketing system (like Jira or a dedicated Slack channel with workflows) where requests are routed to the right team from the start. A simple table in a shared Confluence page can work wonders:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;If your request is about…&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;The team to contact is…&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Site is down or slow (e.g., &lt;code&gt;503 Service Unavailable&lt;/code&gt; on the main app)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;DevOps/SRE&lt;/strong&gt; (via PagerDuty)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Access to a system (e.g., AWS Console, GitHub)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;DevOps&lt;/strong&gt; (via Jira Ticket)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sales data, conversion rates, customer analytics&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Business Intelligence / Analytics&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Which affiliate products to promote&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Marketing / Affiliate Management&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This isn’t about building a wall; it’s about building a directory. It helps them get faster answers and protects your team’s focus for critical engineering work.&lt;/p&gt;

&lt;h3&gt;
  
  
  The ‘Nuclear’ Option: Malicious Compliance as an Educational Tool
&lt;/h3&gt;

&lt;p&gt;Okay, let’s say they won’t take “no” for an answer. Sometimes, the only way to make someone understand they’re asking the wrong question is to give them the answer from &lt;em&gt;your&lt;/em&gt; world. Take their request literally and translate it into a full-blown engineering project plan. It’s an exercise in malicious compliance that can be surprisingly effective.&lt;/p&gt;

&lt;p&gt;The conversation goes like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Them:&lt;/strong&gt; “No, we really need a tech perspective on which ClickBank product converts best.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You:&lt;/strong&gt; “Okay, I understand. You want a data-driven, technical analysis. To do this properly, we’ll need to instrument the funnels. I’ll scope out a project to build a data ingestion pipeline. We can use event-driven tracking pixels on the landing pages, stream the data via AWS Kinesis to a Redshift cluster for warehousing. From there, I’ll build out some Grafana dashboards to visualize the A/B test results in real-time. I’ll need to provision a new VPC, set up the IAM roles, and configure the ETL jobs. The initial infrastructure cost will be about $3,000/month, not including engineering time. I can have a project proposal ready by Thursday. Who should I bill this to?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nine times out of ten, their eyes will glaze over by the time you say “Kinesis,” and they will suddenly realize they should probably just go ask their manager. You’ve answered their question, but you’ve done it in a language that clearly demonstrates that this is a complex engineering task, not a simple opinion. You’ve shown them the cost of asking the wrong person, and they’ll likely never make that mistake again.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nA" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Notion AI is too expensive for users who only need AI functionality.</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 20:24:47 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-notion-ai-is-too-expensive-for-users-who-only-need-ai-functionality-ofm</link>
      <guid>https://vibe.forem.com/techresolve/solved-notion-ai-is-too-expensive-for-users-who-only-need-ai-functionality-ofm</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Notion AI’s high cost stems from feature bundling, forcing users to pay for an entire suite for just AI functionality. Engineers can circumvent this by employing browser extensions, building API bridges between Notion and external AI services, or migrating to a decoupled knowledge management stack for cost-effective, controlled AI integration.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Notion’s AI feature bundling is a business strategy to increase Average Revenue Per User (ARPU), not a technical limitation, by tying a desirable feature to higher-tier plans.&lt;/li&gt;
&lt;li&gt;Building an API bridge using Notion’s API and external AI APIs (e.g., OpenAI, Anthropic) allows for custom, cost-effective AI integration with full control over models and prompts.&lt;/li&gt;
&lt;li&gt;The ‘nuclear option’ involves decoupling knowledge management from AI tools by migrating to a modular stack like Obsidian, which stores local Markdown files, ensuring vendor independence and data ownership.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feeling trapped by expensive, bundled features? This post breaks down why companies like Notion bundle their AI and provides three practical, real-world solutions to get the functionality you need without the hefty price tag.&lt;/p&gt;

&lt;h1&gt;
  
  
  Notion AI is a Great Feature, But I’m Not Paying For the Whole Suite to Get It
&lt;/h1&gt;

&lt;p&gt;I remember a few years back, we were managing a critical database cluster, something like &lt;code&gt;prod-reporting-db-01&lt;/code&gt;, and all we needed was a simple log forwarding agent to ship our slow query logs to our observability platform. The cloud provider’s solution was perfect, but you couldn’t just buy the agent. No, you had to upgrade to their “Enterprise Advanced Security &amp;amp; Threat Detection Suite” for an eye-watering five-figure sum per year. We just wanted one little feature, and they wanted us to buy the whole theme park. This is exactly what I feel when I see the Reddit threads about Notion AI. It’s a fantastic feature locked behind a subscription that includes a dozen other things most of us will never touch. It’s frustrating, it feels wasteful, and it’s a problem we can engineer our way out of.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, Let’s Be Real About the “Why”
&lt;/h2&gt;

&lt;p&gt;This isn’t a technical problem; it’s a business one. It’s called feature bundling. The goal is to increase the Average Revenue Per User (ARPU). By tying a highly desirable feature (AI) to their top-tier plan, they force the upgrade. They’re betting that the convenience of an integrated solution is worth more to you than the cost. For some, it is. For engineers who like to control their stack and optimize for cost and efficiency? Not so much. It’s a deliberate choice to package value in a way that benefits their bottom line, not necessarily your workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1: The Quick Fix (And a Little Hacky)
&lt;/h2&gt;

&lt;p&gt;If you need AI functionality &lt;em&gt;right now&lt;/em&gt; and don’t want to migrate or write a line of code, your best bet is to leverage a browser extension that brings the AI to you. Many extensions can read the context of your current page (your Notion doc) and let you interact with an external AI model like ChatGPT or Claude.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; You highlight text in your Notion page, use a hotkey, and a sidebar or popup appears connected to your own AI account (like OpenAI). You’re essentially using a third-party AI as an “overlay” on Notion. It’s not perfectly integrated, and you’ll be copy-pasting the results back into your page, but it gets the job done for quick summaries, brainstorming, or rewrites.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; Be careful with these extensions. You are sending your page data to a third party. For personal notes, it’s probably fine. For sensitive corporate data from ‘TechResolve’, this is a non-starter and a security risk. Always check your company’s policy on data handling.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solution 2: The DevOps Fix (The API Bridge)
&lt;/h2&gt;

&lt;p&gt;This is my preferred method. If a service gives you an API, you have an escape hatch. Notion has a pretty solid API, and so do all the major AI providers. We can build a simple bridge between them. You get to use a more powerful (and often cheaper, on a per-use basis) AI model and have complete control over the process.&lt;/p&gt;

&lt;p&gt;The idea is to create a small script that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pulls the content from a specific Notion page using the Notion API.&lt;/li&gt;
&lt;li&gt;Sends that content to an AI API (e.g., OpenAI’s GPT-4o or Anthropic’s Claude 3 Sonnet).&lt;/li&gt;
&lt;li&gt;Takes the AI-generated result and appends it back to the original Notion page or a new one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here’s what some Python pseudo-code for that might look like. Don’t just copy-paste this; it’s a blueprint to get you thinking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import notion_client
import openai

# WARNING: Use environment variables or a secret manager in production!
NOTION_API_KEY = "your_notion_api_key"
OPENAI_API_KEY = "your_openai_api_key"
PAGE_ID_TO_PROCESS = "the_id_of_your_notion_page"

# Initialize clients
notion = notion_client.Client(auth=NOTION_API_KEY)
openai.api_key = OPENAI_API_KEY

def get_page_content(page_id):
    # Simplified: you'd need to handle pagination and block types
    response = notion.blocks.children.list(block_id=page_id)
    content = ""
    for block in response["results"]:
        if block["type"] == "paragraph":
            content += block["paragraph"]["rich_text"][0]["plain_text"]
    return content

def summarize_text_with_ai(text):
    response = openai.Completion.create(
      engine="text-davinci-003", # Or a newer chat model
      prompt=f"Please summarize the following text:\n\n{text}",
      max_tokens=150
    )
    return response.choices[0].text.strip()

# --- Main Execution ---
page_content = get_page_content(PAGE_ID_TO_PROCESS)
summary = summarize_text_with_ai(page_content)

# Now, use the Notion API to append the summary as a new block
notion.blocks.children.append(
    block_id=PAGE_ID_TO_PROCESS,
    children=[
        {
            "object": "block",
            "type": "paragraph",
            "paragraph": {
                "rich_text": [{"type": "text", "text": {"content": f"AI Summary: {summary}"}}]
            }
        }
    ]
)
print("Summary appended to Notion page!")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you ultimate flexibility. You can choose your model, customize your prompts, and trigger it however you want—a cron job, a webhook, or a local script. You only pay for what you use on the AI side, which is almost always cheaper than a fixed monthly subscription.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3: The Architect’s Fix (The ‘Nuclear’ Option)
&lt;/h2&gt;

&lt;p&gt;Sometimes, a tool’s business model is so fundamentally misaligned with your needs that the only real solution is to migrate away. The “nuclear option” is to decouple your knowledge management from your AI tools entirely.&lt;/p&gt;

&lt;p&gt;This means moving from a monolithic, all-in-one tool like Notion to a more modular stack. For knowledge management, you could use something like Obsidian, which stores your notes as local Markdown files. This is great for version control with Git and gives you true ownership of your data. Then, you integrate that with your AI tool of choice, using the API method described above or other community plugins.&lt;/p&gt;

&lt;p&gt;This is a big lift, no doubt about it. But it solves the core problem permanently: you are no longer at the mercy of a single vendor’s pricing and feature-bundling decisions. You own the stack.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. Browser Extension&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Fast, easy, no setup.&lt;/td&gt;
&lt;td&gt;Hacky, manual copy/paste, potential security risks.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. API Bridge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Full control, cost-effective (pay-as-you-go), customizable.&lt;/td&gt;
&lt;td&gt;Requires coding skills, API key management, initial setup time.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. Decouple Stack&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Permanent solution, vendor-agnostic, full data ownership.&lt;/td&gt;
&lt;td&gt;High effort, requires migration, learning new tools.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At the end of the day, there’s no single right answer. But as an engineer, you have options beyond just clicking “Upgrade”. Evaluate the tradeoffs, pick your path, and build the workflow that actually works for you—not just the one they’re trying to sell you.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nz" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Pricing changes for GitHub Actions</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 19:20:58 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-pricing-changes-for-github-actions-4623</link>
      <guid>https://vibe.forem.com/techresolve/solved-pricing-changes-for-github-actions-4623</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; GitHub now bills organizations for Actions triggered from forks of private repositories, leading to unexpected cost spikes. Solutions include immediately disabling fork workflows, setting spending limits, optimizing runner types, or implementing self-hosted runners for greater control.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;GitHub now bills organizations for Actions initiated from forks of private repositories, a significant shift from previous assumptions.&lt;/li&gt;
&lt;li&gt;An immediate, though blunt, solution to stop billing overruns is to disable fork pull request workflows at the GitHub Organization level.&lt;/li&gt;
&lt;li&gt;Permanent cost control involves setting strict spending limits and optimizing workflow runner types (e.g., using &lt;code&gt;ubuntu-22.04&lt;/code&gt; instead of &lt;code&gt;ubuntu-latest&lt;/code&gt;) to match job requirements.&lt;/li&gt;
&lt;li&gt;For massive usage or specific needs, self-hosted runners offer ultimate cost control and performance customization but introduce significant maintenance and security responsibilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub Actions pricing changes can lead to unexpected bills, especially from forked private repositories. This guide provides immediate, permanent, and advanced solutions from a senior engineer to control your CI/CD costs.&lt;/p&gt;

&lt;h1&gt;
  
  
  GitHub Actions is Costing You a Fortune. Let’s Fix That.
&lt;/h1&gt;

&lt;p&gt;I still remember the Monday morning alert from finance. Our cloud bill had a spike that looked more like a mountain. After a frantic half-hour of digging, we found the culprit: a junior engineer had forked one of our legacy monolithic repos over the weekend to test a small change. They didn’t realize the fork inherited our entire suite of CI/CD workflows, which, due to a poorly configured cron trigger, ran every five minutes. For 48 hours straight. On &lt;code&gt;ubuntu-latest-4-cores&lt;/code&gt; runners. We burned through our entire monthly GitHub Actions budget before most people had their first coffee. It was an expensive, painful lesson in just how easily these costs can spiral out of control if you aren’t paying attention.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, What Changed? The Root of the Billing Pain
&lt;/h2&gt;

&lt;p&gt;For a long time, the community operated under the assumption that Actions running on forks were “free,” especially in the context of open-source collaboration. The mental model was simple: the contributor uses their own Actions minutes. But recently, GitHub clarified and began enforcing a policy that hits organizations directly: &lt;strong&gt;for private repositories, your organization is now billed for Actions initiated from forks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think about that. Any user with read access can fork your private repo, push a commit to their fork, and trigger your workflows using &lt;em&gt;your&lt;/em&gt; organization’s paid minutes. While there are some safeguards, it’s a significant shift that turns every fork into a potential drain on your budget. Combine this with the generous, but finite, pool of free minutes and the premium cost of larger runners, and you have a perfect recipe for a billing surprise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stopping the Bleed: Three Levels of Defense
&lt;/h2&gt;

&lt;p&gt;When you’re facing a cost overrun, you need a plan. Here are the three approaches we use at TechResolve, from pulling the emergency brake to building a long-term, cost-effective CI/CD platform.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 1: The Quick Fix (Triage Mode)
&lt;/h3&gt;

&lt;p&gt;The first thing to do when the house is on fire is to put out the fire. The fastest way to stop the bleeding from forked repos is to disable them at the organization level. This is a blunt instrument, but it’s effective immediately.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to do it:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to your GitHub Organization’s &lt;strong&gt;Settings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the left sidebar, click &lt;strong&gt;Actions&lt;/strong&gt;, then &lt;strong&gt;General&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Under “Fork pull request workflows from outside collaborators”, select &lt;strong&gt;Disable workflows&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Scroll down to the “Fork pull request workflows” policy and select &lt;strong&gt;Disable for all repositories&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Hit &lt;strong&gt;Save&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This stops the immediate problem. No workflows will run on forks of your private repos, period. Of course, this might break the workflow for your external contributors or even internal teams that use a fork-based model, but it gives you breathing room to implement a better, more permanent solution.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This is a sledgehammer approach. It stops the billing issue cold, but it also stops legitimate development workflows. Use this to stop an active billing incident, but don’t leave it this way forever if you rely on contributions from forks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Solution 2: The Permanent Fix (The Right Way)
&lt;/h3&gt;

&lt;p&gt;Once you’ve stopped the immediate bleeding, it’s time to set up proper guardrails. This involves setting strict spending limits and using cheaper runners where possible.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;set a spending limit&lt;/strong&gt;. Even a limit of $1 is infinitely better than an unlimited budget. This acts as a circuit breaker. If a rogue workflow goes wild, it will hit the cap and stop, preventing a four or five-figure bill. You’ll get a notification, and you can then decide whether to increase the limit or investigate the cause.&lt;/p&gt;

&lt;p&gt;Second, let’s optimize the workflows themselves. Does your linter &lt;em&gt;really&lt;/em&gt; need a 4-core machine? Can your unit tests run on a standard &lt;code&gt;ubuntu-latest&lt;/code&gt; runner instead of a larger, more expensive one? Shaving a few cents off each run adds up to hundreds or thousands of dollars over a month across dozens of repos.&lt;/p&gt;

&lt;p&gt;A simple workflow change looks like this. Instead of the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  build:
    runs-on: ubuntu-latest # This can default to a more expensive machine
    steps:
      ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be explicit about using the most cost-effective runner that can do the job:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jobs:
  build:
    # Use a more specific, and potentially cheaper, runner if it fits your needs.
    # Check GitHub's documentation for the latest runner labels and specs.
    runs-on: ubuntu-22.04 
    steps:
      ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This combination of a hard spending limit and right-sizing your runners is the most sustainable way to manage costs without resorting to drastic measures.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 3: The ‘Nuclear’ Option (Self-Hosting)
&lt;/h3&gt;

&lt;p&gt;If your Actions usage is massive, or you have specific compliance or hardware needs (like GPU access), the ultimate cost-control move is to use self-hosted runners. Instead of paying GitHub per minute, you’re just paying for the compute on your own infrastructure (AWS, Azure, GCP, or even on-prem servers like &lt;code&gt;build-agent-k8s-pod-xyz&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This gives you total control over cost and environment. You can use cheap spot instances, autoscale your runners based on demand, and customize the environment with any software you need. However, it comes with a huge trade-off: &lt;strong&gt;you are now responsible for securing, maintaining, and patching these machines.&lt;/strong&gt; Running code from a forked PR on your own infrastructure is a massive security risk if not handled properly. You need to treat these runners as ephemeral, single-use, and heavily firewalled.&lt;/p&gt;

&lt;p&gt;Here’s a quick breakdown of the trade-offs:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Factor&lt;/th&gt;
&lt;th&gt;GitHub-Hosted Runners&lt;/th&gt;
&lt;th&gt;Self-Hosted Runners&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cost&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Pay-per-minute. Can be high and unpredictable.&lt;/td&gt;
&lt;td&gt;Pay for your own compute. Can be very low with optimization (e.g., spot instances).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Zero. Managed entirely by GitHub.&lt;/td&gt;
&lt;td&gt;High. You are responsible for patching, scaling, and security.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Handled by GitHub. Isolated environments for each job.&lt;/td&gt;
&lt;td&gt;Your responsibility. High risk if running untrusted code from public forks without proper sandboxing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Limited to available GitHub machine types.&lt;/td&gt;
&lt;td&gt;Unlimited. You can use any machine size or type (e.g., GPU, ARM).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Moving to self-hosted runners is a major architectural decision, not just a quick fix. But for large organizations, the long-term cost savings can be immense. We use a hybrid model at TechResolve: critical production deployments use secure, hardened self-hosted runners, while general PR checks and linting run on the cheaper GitHub-hosted machines with a strict spending cap. It’s the best of both worlds.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-ny" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Why does every email builder still feel so slow?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 19:18:28 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-why-does-every-email-builder-still-feel-so-slow-324n</link>
      <guid>https://vibe.forem.com/techresolve/solved-why-does-every-email-builder-still-feel-so-slow-324n</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Email builders often suffer from sluggish performance due to I/O bottlenecks, not CPU or RAM limitations, as applications spend significant time waiting for disk operations. Solutions involve upgrading disk IOPS for immediate relief, offloading static assets to object storage like S3 for a permanent architectural fix, or implementing in-memory caches like Redis for extreme performance needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Application slowness, despite healthy CPU and RAM, frequently indicates an I/O bottleneck, where the application is ‘starved’ waiting for disk operations, identifiable with tools like &lt;code&gt;iotop&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Offloading static assets (images, templates) to dedicated object storage services like Amazon S3 or Google Cloud Storage is the ‘correct architecture’ for long-term performance, freeing up the server’s local disk I/O for application logic.&lt;/li&gt;
&lt;li&gt;Implementing an in-memory cache like Redis or Memcached can provide sub-millisecond access for frequently accessed ‘hot data’ but introduces significant architectural complexity, particularly concerning cache invalidation, and should be reserved for extreme performance requirements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tired of sluggish email builders? Uncover the hidden I/O bottlenecks killing your performance and learn three practical DevOps solutions—from quick disk upgrades to robust architectural refactoring—to make your tools fly again.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why is Our Email Builder Still So Slow? A DevOps War Story
&lt;/h1&gt;

&lt;p&gt;I still remember the 3 AM PagerDuty alert. Not for a downed server, but a Slack message from our Head of Marketing. The subject: “URGENT: Black Friday Campaign Launch Blocked.” I jumped on a call and found the entire marketing team staring at a loading spinner. Our internal email builder, the one we built to give them creative freedom, was taking minutes—literally minutes—to load a template or save a small image change. The servers looked fine: CPU was idling, memory was plentiful. Yet the application felt like it was running through mud. That night, I learned a lesson that every DevOps engineer eventually learns the hard way: &lt;strong&gt;it’s almost always the I/O.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Culprit: You’re Starved for I/O, Not CPU
&lt;/h2&gt;

&lt;p&gt;When an application like an email builder feels slow but the main server metrics (CPU, RAM) look healthy, it’s easy to get confused. We instinctively want to throw more processing power at the problem. But in my experience, nine times out of ten, the bottleneck isn’t processing; it’s the time the application spends waiting for the disk.&lt;/p&gt;

&lt;p&gt;Think about what an email builder does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It reads template files from a disk.&lt;/li&gt;
&lt;li&gt;It writes new versions of those templates to a disk.&lt;/li&gt;
&lt;li&gt;It uploads, processes, and saves images to a disk.&lt;/li&gt;
&lt;li&gt;It pulls user data from a database, which itself is constantly reading and writing to its own disk.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all Input/Output (I/O) operations. When your application is running on a server with a slow or over-utilized disk (like a standard, general-purpose cloud volume), every one of these actions joins a queue. Your powerful CPU sits there, twiddling its thumbs, waiting for the disk to deliver the data it needs. The application isn’t slow; it’s &lt;strong&gt;starved&lt;/strong&gt;. We confirmed it with a simple &lt;code&gt;iotop&lt;/code&gt; command on the server, which showed our main application process at 99% I/O wait.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fixes: From a Band-Aid to a Cure
&lt;/h2&gt;

&lt;p&gt;Okay, so we know the problem is disk I/O. How do we fix it? I’ve seen teams handle this in a few ways, ranging from a quick-and-dirty fix to a proper architectural overhaul. Here’s my playbook.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Quick Fix: Upgrade The Disk
&lt;/h3&gt;

&lt;p&gt;This is the “stop the bleeding” approach. If your application is running on a cloud VM, the fastest way to alleviate I/O pain is to provision a faster disk. In AWS, this means moving from a General Purpose SSD (like &lt;code&gt;gp2&lt;/code&gt; or &lt;code&gt;gp3&lt;/code&gt;) to a Provisioned IOPS SSD (&lt;code&gt;io1&lt;/code&gt; or &lt;code&gt;io2&lt;/code&gt;). You’re essentially paying for a dedicated, high-speed lane for your data.&lt;/p&gt;

&lt;p&gt;It’s a straightforward infrastructure change, often requiring little to no downtime. You’re just telling your cloud provider, “Give me more disk speed, and send me the bill.”&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; This is a perfectly valid short-term solution. When the marketing team is blocked from sending a multi-million dollar campaign, you don’t have time to refactor code. You apply the expensive band-aid, get them working, and then you plan the real fix.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. The Permanent Fix: Offload Assets to Object Storage
&lt;/h3&gt;

&lt;p&gt;The real, long-term solution is to stop treating your server’s local disk as a filing cabinet for everything. Your web server’s primary job is to run application code, not to be a high-performance file server for static assets like images, CSS, and HTML templates.&lt;/p&gt;

&lt;p&gt;The correct architecture is to offload all of that static content to a dedicated object storage service, like &lt;strong&gt;Amazon S3&lt;/strong&gt; or Google Cloud Storage.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The application logic is changed. When a user uploads an image, the app doesn’t save it to &lt;code&gt;/var/www/uploads&lt;/code&gt;. Instead, it uses the cloud SDK to upload it directly to an S3 bucket.&lt;/li&gt;
&lt;li&gt;The database (our trusty &lt;code&gt;prod-db-01&lt;/code&gt;) doesn’t store the asset; it stores a pointer to it—the S3 object key.&lt;/li&gt;
&lt;li&gt;When a template is loaded, the application fetches the assets from S3, not the local disk.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This change fundamentally frees up your server’s disk I/O to do what it’s supposed to: run the application and serve dynamic requests. The heavy lifting of storing and serving files is moved to a service built for exactly that purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The ‘Nuclear’ Option: Implement an In-Memory Cache
&lt;/h3&gt;

&lt;p&gt;What if even S3 isn’t fast enough? This can happen with extremely high-traffic builders where the same few templates are accessed thousands of time per minute. The latency of fetching from S3, while low, can still add up. For this scenario, we bring in an in-memory cache like &lt;strong&gt;Redis&lt;/strong&gt; or &lt;strong&gt;Memcached&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The logic becomes a waterfall:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function get_template(template_id) {
    // 1. Check the super-fast in-memory cache first
    data = redis.get(f"template:{template_id}");
    if (data) {
        return data; // Cache Hit! Super fast.
    }

    // 2. If not in cache, get it from the reliable source (S3)
    data = fetch_from_s3(f"templates/{template_id}.html");
    if (data) {
        // 3. Put it in the cache for the *next* request
        // Set a Time-To-Live (TTL) of 1 hour (3600s)
        redis.set(f"template:{template_id}", data, ex=3600);
    }

    return data;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; Don’t jump to this solution first. It adds significant complexity to your architecture. Cache invalidation (“How do I make sure the cache is cleared when a template is updated?”) is one of the classic hard problems in computer science. Only use this when you have a clear performance need for sub-millisecond access to hot data.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparing The Solutions
&lt;/h2&gt;

&lt;p&gt;To make it clearer, here’s how I’d break down the options for my team:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Solution&lt;/th&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;th&gt;Long-Term Viability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. Upgrade Disk (IOPS)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low (Infra change)&lt;/td&gt;
&lt;td&gt;Medium to High (Recurring)&lt;/td&gt;
&lt;td&gt;Poor (It’s a band-aid)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. Offload to S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium (Code change)&lt;/td&gt;
&lt;td&gt;Low (S3 is cheap)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Excellent (Correct architecture)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. In-Memory Cache (Redis)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High (New service + code change)&lt;/td&gt;
&lt;td&gt;Medium (Redis server cost)&lt;/td&gt;
&lt;td&gt;Situational (For extreme performance needs)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That night, we went with Option 1 to get the campaign out the door. But the very next sprint, we implemented Option 2. The builder has been flying ever since, and my PagerDuty has been wonderfully quiet. The moral of the story? Next time your application feels slow, stop looking at the CPU meter and start investigating your I/O. Your sanity will thank you for it.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nx" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Inherited a legacy project with zero API docs any fast way to map all endpoints?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 18:36:49 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-inherited-a-legacy-project-with-zero-api-docs-any-fast-way-to-map-all-endpoints-36hm</link>
      <guid>https://vibe.forem.com/techresolve/solved-inherited-a-legacy-project-with-zero-api-docs-any-fast-way-to-map-all-endpoints-36hm</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Inheriting a legacy API with zero documentation poses significant risks, as critical services can depend on unknown endpoints. This guide provides three battle-tested methods—log diving, code analysis, and proxy-based reverse-engineering—to quickly map all API endpoints and regain control of the system.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Web server access logs (e.g., Nginx, Apache) are a quick and reliable source for identifying actively used API endpoints and their frequency in production.&lt;/li&gt;
&lt;li&gt;Analyzing framework-specific routing files (e.g., &lt;code&gt;config/routes.rb&lt;/code&gt; for Rails, &lt;code&gt;urls.py&lt;/code&gt; for Django) provides the definitive list of all defined endpoints and can be used to generate OpenAPI specifications.&lt;/li&gt;
&lt;li&gt;Man-in-the-middle proxies like mitmproxy or Charles Proxy can capture complete request/response details, including headers and payloads, crucial for understanding undocumented data contracts in staging environments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Inherited a legacy API with zero documentation? Discover three battle-tested methods—from quick log-parsing to advanced reverse-engineering—to map every endpoint and regain control of your system.&lt;/p&gt;

&lt;h1&gt;
  
  
  So You Inherited an API with No Docs. Now What?
&lt;/h1&gt;

&lt;p&gt;I remember it like it was yesterday. 3:17 AM. My phone starts screaming—the unmistakable PagerDuty wail that sends a jolt of ice through your veins. The primary payment processing service was down. Hard. After a frantic 20 minutes of digging, we found the culprit: a critical downstream service was hammering a deprecated, undocumented API endpoint on the payment service. A recent cleanup deployment had finally removed it, and nobody knew this other service even depended on it. We spent the next two hours rolling back and patching, all because of an endpoint that existed only in the original developer’s memory. We’ve all been there, handed the keys to a kingdom with no map. It’s frustrating, dangerous, and frankly, a rite of passage in this industry.&lt;/p&gt;

&lt;h2&gt;
  
  
  The “Why”: How We Get Here
&lt;/h2&gt;

&lt;p&gt;Let’s be honest, nobody sets out to create an undocumented system. This mess is a symptom of a deeper issue: technical debt. It’s the result of tight deadlines, developers leaving the company, re-orgs, or the classic “we’ll document it later” promise that never gets fulfilled. The code was written to solve a problem *right now*, and the long-term maintainability was an afterthought. Understanding this isn’t about placing blame; it’s about recognizing the pattern so we can fix it for good.&lt;/p&gt;

&lt;p&gt;So, you’re stuck. You have a black box, you know it’s important, but you have no idea what’s inside. Let’s pry it open. Here are three methods I’ve used, ranging from a quick-and-dirty fix to a full-blown architectural deep dive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1: The Quick &amp;amp; Dirty (Log Diving)
&lt;/h2&gt;

&lt;p&gt;Your first move shouldn’t be to clone the repo. It should be to look at what’s actually happening in production *right now*. Your web server access logs are a goldmine of truth. They can’t lie. They record every single request that hits your server.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Tactic:&lt;/strong&gt; SSH into one of your API servers (say, &lt;code&gt;api-prod-west-03a&lt;/code&gt;) and start grepping the access logs (e.g., &lt;code&gt;/var/log/nginx/access.log&lt;/code&gt; or &lt;code&gt;/var/log/httpd/access_log&lt;/code&gt;). A simple one-liner can give you a surprisingly clear picture of your most-used endpoints.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# This command will give you a count of unique requests (method + path)
# sorted by the most frequently used.

cat /var/log/nginx/access.log | awk '{print $6 " " $7}' | sort | uniq -c | sort -rn | head -n 20
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will give you an output like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 113821 "POST /api/v1/session"
  98345 "GET /api/v1/user/status"
  50123 "GET /api/v2/items/search"
  12055 "POST /api/v1/order"
   ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Pro Tip:&lt;/strong&gt; This only shows you what’s being &lt;em&gt;used&lt;/em&gt;. It won’t reveal forgotten, dormant, or legacy endpoints that are still active in the code but aren’t being called. It’s a great starting point for a priority list, not a complete map.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solution 2: The Source of Truth (Code Analysis)
&lt;/h2&gt;

&lt;p&gt;Once you have a baseline from the logs, it’s time to dive into the code. This is where you’ll find the ground truth. Every major web framework has a “router” file or a mechanism that defines the valid endpoints.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;Ruby on Rails&lt;/strong&gt;, look for &lt;code&gt;config/routes.rb&lt;/code&gt;. Running &lt;code&gt;rake routes&lt;/code&gt; in the terminal will literally print out every single defined route.&lt;/li&gt;
&lt;li&gt;In a &lt;strong&gt;Python/Django&lt;/strong&gt; project, check the main &lt;code&gt;urls.py&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;Node.js/Express&lt;/strong&gt;, you’ll need to trace how the app uses &lt;code&gt;app.get()&lt;/code&gt;, &lt;code&gt;app.post()&lt;/code&gt;, etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Tactic:&lt;/strong&gt; Get read-only access to the Git repository. Clone it, and start searching for the routing definitions. This is also the perfect time to introduce automated tooling. You can often generate an OpenAPI (formerly Swagger) specification directly from the code. Tools like &lt;strong&gt;Swashbuckle&lt;/strong&gt; for .NET or libraries that use code annotations in Java (JAX-RS) can build interactive documentation for you.&lt;/p&gt;

&lt;p&gt;This is your path to a permanent fix. Once you have a spec file, you can check it into version control and use it to generate client SDKs, documentation websites, and even automated contract tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 3: The ‘Nuclear’ Option (Reverse-Engineering with a Proxy)
&lt;/h2&gt;

&lt;p&gt;Sometimes the code is an unreadable mess, and the logs don’t tell the whole story. What about the request headers? The exact JSON payload structures? This is when you need to watch the traffic live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Tactic:&lt;/strong&gt; Set up a man-in-the-middle (MITM) proxy like &lt;strong&gt;mitmproxy&lt;/strong&gt; or &lt;strong&gt;Charles Proxy&lt;/strong&gt; in a staging or test environment. You configure your client application (or a suite of integration tests) to route its traffic through the proxy before it hits your API server. The proxy then logs *everything*—every header, every byte of the payload, every cookie, every response code.&lt;/p&gt;

&lt;p&gt;This is incredibly powerful for understanding complex interactions. You’re not just seeing the endpoint path; you’re seeing the entire conversation. It’s invaluable for replicating behavior and understanding undocumented data contracts.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This is a high-effort, high-reward approach. Setting up the proxy and SSL interception correctly can be tricky, and you absolutely should NOT run this in production unless you are in a fire-fight and have exhausted all other options. An Application Performance Monitoring (APM) tool like DataDog or New Relic can provide a safer, production-ready version of this by tracing requests as they flow through your system.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comparing The Approaches
&lt;/h2&gt;

&lt;p&gt;There’s no single right answer; the best method depends on your situation. Here’s a quick breakdown:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Effort&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Best For…&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. Log Diving&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Partial (shows usage)&lt;/td&gt;
&lt;td&gt;Getting a quick map of the most critical, active endpoints in minutes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. Code Analysis&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;td&gt;High (source of truth)&lt;/td&gt;
&lt;td&gt;Creating permanent, maintainable documentation and finding all possible routes.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. Proxy/Reverse-Engineering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Complete (includes payloads)&lt;/td&gt;
&lt;td&gt;When you need to understand the full data contract and behavior of a true “black box” system.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ultimately, inheriting a poorly documented system is a challenge, but it’s also an opportunity. An opportunity to stabilize it, to document it, and to leave it in a much better state than you found it. Start with the logs, move to the code, and don’t be afraid to break out the big guns if you have to. Good luck.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nw" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Help: DNS Broken in 29.1</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 18:34:18 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-help-dns-broken-in-291-4ehj</link>
      <guid>https://vibe.forem.com/techresolve/solved-help-dns-broken-in-291-4ehj</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; After a system update, DNS resolution can fail due to &lt;code&gt;systemd-resolved&lt;/code&gt; losing its upstream DNS configuration, even if direct IP pings work. The recommended solution involves configuring &lt;code&gt;/etc/systemd/resolved.conf&lt;/code&gt; to specify DNS servers and domains, ensuring &lt;code&gt;systemd-resolved&lt;/code&gt; correctly manages &lt;code&gt;/etc/resolv.conf&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;In modern Linux, &lt;code&gt;/etc/resolv.conf&lt;/code&gt; is often a symlink managed by &lt;code&gt;systemd-resolved&lt;/code&gt;, which acts as a local DNS stub resolver on &lt;code&gt;127.0.0.53&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;System updates, network manager changes, or cloud-init scripts can overwrite &lt;code&gt;systemd-resolved&lt;/code&gt;’s internal configuration, leading to name resolution failures.&lt;/li&gt;
&lt;li&gt;The most robust solution is to configure &lt;code&gt;systemd-resolved&lt;/code&gt; directly via &lt;code&gt;/etc/systemd/resolved.conf&lt;/code&gt; by setting &lt;code&gt;DNS&lt;/code&gt;, &lt;code&gt;FallbackDNS&lt;/code&gt;, and &lt;code&gt;Domains&lt;/code&gt; directives, then restarting the service.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Struggling with DNS resolution failures after a system update? Learn the root cause of the infamous &lt;code&gt;systemd-resolved&lt;/code&gt; issue and explore three practical solutions, from a quick temporary hack to a permanent configuration fix.&lt;/p&gt;

&lt;h1&gt;
  
  
  So, an Update Broke Your DNS. Let’s Talk About &lt;code&gt;systemd-resolved&lt;/code&gt;.
&lt;/h1&gt;

&lt;p&gt;I remember a 2 AM outage call. The monitoring dashboards were screaming red, claiming half our services were down. But I could SSH into the boxes just fine. &lt;code&gt;ping 8.8.8.8&lt;/code&gt;? Worked like a charm. &lt;code&gt;ping prod-db-01.internal.mycorp&lt;/code&gt;? Timeout. Every single time. The new deployment hadn’t touched DNS, so what gives? The culprit, as it often is these days, was a minor OS patch that reset our network config, and a “helpful” little service called &lt;code&gt;systemd-resolved&lt;/code&gt; decided my carefully crafted &lt;code&gt;/etc/resolv.conf&lt;/code&gt; was merely a suggestion. If that feeling of dread sounds familiar, you’re in the right place. Let’s get this sorted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s Actually Happening Here? The “Why” Behind the Pain
&lt;/h2&gt;

&lt;p&gt;In modern Linux distributions, you’re not writing to &lt;code&gt;/etc/resolv.conf&lt;/code&gt; directly anymore. That file is often just a symlink managed by &lt;code&gt;systemd-resolved&lt;/code&gt;, a local DNS stub resolver. The goal is noble: to manage DNS settings more dynamically, especially for things like VPNs. In reality, it means your server now points all its DNS queries to a local address, usually &lt;code&gt;127.0.0.53&lt;/code&gt;, and lets &lt;code&gt;systemd-resolved&lt;/code&gt; handle the forwarding.&lt;/p&gt;

&lt;p&gt;The problem arises when an update, a network manager change, or a cloud-init script overwrites the configuration that &lt;code&gt;systemd-resolved&lt;/code&gt; itself uses. When that happens, your server can still talk to IPs, but it has no idea how to look up a name. It’s like having a phone that can call numbers but has lost its entire contact list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Taming the Beast: Three Ways to Fix Your DNS
&lt;/h2&gt;

&lt;p&gt;We’ve got options, ranging from a quick-and-dirty fix to get you back online to the “proper” long-term solution. I’ve used all three in different situations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 1: The “Get It Working NOW” Fix (chattr)
&lt;/h3&gt;

&lt;p&gt;This is the classic “brute force” method. We’re going to manually create the &lt;code&gt;/etc/resolv.conf&lt;/code&gt; file with the correct settings and then make it immutable, preventing any process (including &lt;code&gt;systemd-resolved&lt;/code&gt;) from changing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; First, stop and disable the service that’s causing the problem.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; The real &lt;code&gt;/etc/resolv.conf&lt;/code&gt; is often managed through a symlink. We need to break that link and create a real file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo rm /etc/resolv.conf
sudo nano /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Add your nameservers to the new file. This could be your internal DNS, a public one like Google’s, or your cloud provider’s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Our internal DNS servers
nameserver 10.0.1.10
nameserver 10.0.1.11
# Fallback to Google
nameserver 8.8.8.8
search internal.mycorp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; This is the magic. We lock the file using &lt;code&gt;chattr&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo chattr +i /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Darian’s Take:&lt;/strong&gt; Be warned, this is a hack. It works, and it’ll save you during an outage. But you’re fighting the system, and a future OS update might have unpredictable results. Someone (maybe you, six months from now) will be very confused when they can’t edit that file. Remember to use &lt;code&gt;chattr -i&lt;/code&gt; before you try to change it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Solution 2: The “Do It Right” Permanent Fix (Configure resolved.conf)
&lt;/h3&gt;

&lt;p&gt;This is the method I recommend for production systems. Instead of fighting &lt;code&gt;systemd-resolved&lt;/code&gt;, we’re going to tell it how to behave correctly. This way, we’re working *with* the operating system, not against it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Edit the &lt;code&gt;systemd-resolved&lt;/code&gt; configuration file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo nano /etc/systemd/resolved.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Find the &lt;code&gt;[Resolve]&lt;/code&gt; section. Uncomment and set the &lt;code&gt;DNS&lt;/code&gt;, &lt;code&gt;FallbackDNS&lt;/code&gt;, and &lt;code&gt;Domains&lt;/code&gt; directives. This tells the service which upstream servers to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Resolve]
DNS=10.0.1.10 10.0.1.11
FallbackDNS=8.8.8.8 8.8.4.4
Domains=internal.mycorp mycorp.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; Ensure &lt;code&gt;/etc/resolv.conf&lt;/code&gt; is pointing to the systemd stub file. This is the “correct” symlink setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 4:&lt;/strong&gt; Restart the service to apply the new configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl restart systemd-resolved
sudo systemctl status systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify it’s working with &lt;code&gt;resolvectl status&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Solution 3: The “Nuclear Option” (Disable and Go Old-School)
&lt;/h3&gt;

&lt;p&gt;Sometimes, you just don’t want to deal with it. Maybe you have legacy scripts or configuration management (like an old version of Puppet) that expects to manage &lt;code&gt;/etc/resolv.conf&lt;/code&gt; directly. In this case, you can completely disable &lt;code&gt;systemd-resolved&lt;/code&gt; and revert to a more traditional networking setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1:&lt;/strong&gt; Completely stop and disable the service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl disable systemd-resolved
sudo systemctl stop systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 2:&lt;/strong&gt; Mask the service to prevent any other process from starting it again, ever.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo systemctl mask systemd-resolved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Step 3:&lt;/strong&gt; At this point, you’re on your own. You’ll need to configure your primary networking service (like &lt;code&gt;NetworkManager&lt;/code&gt; or &lt;code&gt;systemd-networkd&lt;/code&gt;) to manage &lt;code&gt;/etc/resolv.conf&lt;/code&gt; for you, or you’ll have to manage it manually as we did in Solution 1 (but without needing &lt;code&gt;chattr&lt;/code&gt;). For &lt;code&gt;NetworkManager&lt;/code&gt;, you’d typically edit &lt;code&gt;/etc/NetworkManager/NetworkManager.conf&lt;/code&gt; and set &lt;code&gt;dns=default&lt;/code&gt; or &lt;code&gt;dns=none&lt;/code&gt; to give you back control.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; This is a major change. Disabling a core systemd component can have side effects, especially with complex networking setups involving VPNs or containers that might rely on its features. Only do this if you know what you’re doing and are prepared to manage DNS resolution yourself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Which Path Should You Choose?
&lt;/h2&gt;

&lt;p&gt;Here’s how I break it down for my team:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Solution&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Best For&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Pros&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Cons&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;1. The Quick Fix (chattr)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Emergency outage mitigation.&lt;/td&gt;
&lt;td&gt;Fast, simple, effective immediately.&lt;/td&gt;
&lt;td&gt;Brittle, confusing for future troubleshooting, fights the OS.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;2. The Permanent Fix (resolved.conf)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Most production systems.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Works with the system, stable, “correct” method.&lt;/td&gt;
&lt;td&gt;Requires understanding &lt;code&gt;systemd&lt;/code&gt; config.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;3. The Nuclear Option (disable)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Systems with specific legacy requirements or for experts who prefer manual control.&lt;/td&gt;
&lt;td&gt;Gives you full, direct control.&lt;/td&gt;
&lt;td&gt;Can have unintended side effects; you lose modern features.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Ultimately, DNS is one of those foundational services that must be rock solid. While it’s frustrating when an update knocks it over, understanding the components at play is the key to building resilient systems. My advice? Take the time to learn Solution 2. Your future self, at 2 AM, will thank you.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nv" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Realistic AI headshots without the wax-museum look any non-tech wins?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 17:38:32 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-realistic-ai-headshots-without-the-wax-museum-look-any-non-tech-wins-f4i</link>
      <guid>https://vibe.forem.com/techresolve/solved-realistic-ai-headshots-without-the-wax-museum-look-any-non-tech-wins-f4i</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AI-generated headshots often look artificial due to model oversmoothing; achieve realism by employing advanced prompt engineering with specific positive and negative prompts, fine-tuning models via LoRA with personal photos, and utilizing hybrid workflows for final touches.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Counteracting Model Oversmoothing: Explicitly include “skin texture” and “pores” in positive prompts and use weighted negative prompts like “(plastic, doll, smooth skin, airbrushed:1.3)” to combat the generic, smoothed-out appearance from base diffusion models.&lt;/li&gt;
&lt;li&gt;Personalized Realism with LoRA: Train a Low-Rank Adaptation (LoRA) model using 15-20 varied personal photos to embed specific facial structures into the AI, enabling consistent and highly realistic generations that generic prompts cannot achieve.&lt;/li&gt;
&lt;li&gt;Optimizing Sampler Settings: Fine-tune CFG Scale (e.g., 6.5 for naturalness) and Denoising Strength (e.g., 0.4-0.6 for img2img) to control the AI’s adherence to the prompt and preserve subtle textures, preventing the “denoising trap” that removes realism.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tired of AI-generated headshots looking like plastic dolls? Learn how to inject realism by fine-tuning models with your own photos (LoRA) and mastering advanced prompting techniques for truly lifelike results.&lt;/p&gt;

&lt;h1&gt;
  
  
  I See Your AI Headshot and Raise You One Uncanny Valley: Escaping the Wax Museum
&lt;/h1&gt;

&lt;p&gt;I still remember the Slack message from Marketing. “Hey Darian, can you spin up that ‘AI thing’ and get us new headshots for the exec team? We need them for the new ‘About Us’ page by Friday.” Simple enough, I thought. I grabbed a few of the CEO’s approved photos from our press kit, fired up a Stable Diffusion instance, and ran a basic prompt. The result? A perfectly coiffed, wrinkle-free, soulless mannequin that looked like our CEO had a horrifying accident at a candle-making factory. It was technically him, but it had zero life. That’s when I knew we had to go deeper than just a simple text prompt.&lt;/p&gt;

&lt;h2&gt;
  
  
  So, Why Does This Keep Happening? The Root of the Plastic Look
&lt;/h2&gt;

&lt;p&gt;Before we jump into the fixes, you need to understand &lt;em&gt;why&lt;/em&gt; this happens. It’s not just “bad AI.” It’s a combination of a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model Oversmoothing:&lt;/strong&gt; Base diffusion models are trained on millions of images. To create a “general” human face, they average out features. This process naturally smooths out imperfections—pores, fine lines, asymmetrical features—which are the very things that make a face look real.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Denoising Trap:&lt;/strong&gt; The core process of diffusion is starting with noise and “denoising” it into an image based on your prompt. If the denoising strength is too high, it wipes away subtle textures, resulting in that glossy, airbrushed finish.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of Specificity:&lt;/strong&gt; A prompt like “photo of a man in a suit” is asking the model to pull from its vast, generic knowledge. It doesn’t know *your* face, it knows the *idea* of a face.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal isn’t to just make a picture; it’s to guide the model away from its generic defaults and force it to add back the chaos of reality. Here’s how we do it in the trenches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution 1: The Quick Fix (Prompt Engineering &amp;amp; Sampler Fu)
&lt;/h2&gt;

&lt;p&gt;This is your first line of defense. It’s fast, requires no custom models, and can often get you 80% of the way there. It’s all about telling the model what to add and, more importantly, what to avoid.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Prompt Breakdown
&lt;/h3&gt;

&lt;p&gt;Instead of a simple prompt, we get hyper-specific and use negative prompts to fight the “wax museum” effect. Let’s assume you’re using a tool like Automatic1111 or any other standard UI.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Positive Prompt:
(photograph of a 40-year-old man), professional headshot, sharp focus, (skin texture:1.2), pores, detailed skin, slight smile, soft studio lighting, Canon EOS 5D Mark IV, 85mm f/1.8 lens

Negative Prompt:
(plastic, doll, cartoon, 3d, render:1.3), painting, art, blurry, oversaturated, smooth skin, airbrushed, unnatural
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the key elements: We’re explicitly asking for “skin texture” and “pores” and using weights &lt;code&gt;(word:1.2)&lt;/code&gt; to increase their importance. In the negative prompt, we’re actively telling it to avoid the things that make images look fake. This is the equivalent of putting guardrails on the generation process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tweak Your Sampler Settings
&lt;/h3&gt;

&lt;p&gt;Don’t just hit “Generate.” Play with these two settings:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Setting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;What it Does &amp;amp; Why You Should Care&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;CFG Scale&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;How strictly the AI follows your prompt. A low value (e.g., 4-6) gives it more creative freedom, which can feel more natural. A high value (e.g., 7-10) sticks closer to your prompt but can feel rigid. Start around &lt;strong&gt;6.5&lt;/strong&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denoising Strength (for img2img)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;If you’re using an existing photo as a base (img2img), this is critical. A value of &lt;strong&gt;0.4-0.6&lt;/strong&gt; will change the style and lighting while preserving the facial structure. Go higher, and you start getting a different person.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Solution 2: The Permanent Fix (Train a LoRA On Yourself)
&lt;/h2&gt;

&lt;p&gt;This is the real deal. When prompt engineering isn’t enough, you need to teach the model &lt;em&gt;exactly&lt;/em&gt; what you look like. We do this by training a LoRA (Low-Rank Adaptation). Think of it as a small, lightweight “plugin” for the main model that contains information about a specific person or style.&lt;/p&gt;

&lt;p&gt;This is where my DevOps hat comes on. You don’t need a massive GPU farm for this. You can rent a GPU instance (like an A100 on GCP or AWS) for an hour or use a cloud service that automates it.&lt;/p&gt;

&lt;h3&gt;
  
  
  The High-Level Workflow:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Gather Your Data:&lt;/strong&gt; Collect 15-20 high-quality, varied photos of yourself. Different angles, different lighting, different expressions. No sunglasses, no hats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepare &amp;amp; Tag:&lt;/strong&gt; Use a tool (like the automated taggers in Kohya_ss) to caption each image. This tells the training process what’s in the picture (e.g., “a photo of dvance_man”). The unique keyword &lt;code&gt;dvance\_man&lt;/code&gt; is what you’ll use to call yourself in the prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Train the LoRA:&lt;/strong&gt; Load your images and captions into a training UI like Kohya_ss. You’ll set parameters like learning rate and number of epochs. This process usually takes 20-40 minutes on a decent GPU. It spits out a small file (e.g., &lt;code&gt;dvance\_man.safetensors&lt;/code&gt;, maybe 100MB).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Generate with Your LoRA:&lt;/strong&gt; Now, you use a special prompt that includes your LoRA.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Positive Prompt:
professional headshot of &amp;lt;lora:dvance_man:0.8&amp;gt;, sharp focus, detailed skin, corporate office background

// The &amp;lt;lora:lora_name:weight&amp;gt; syntax tells the generator to apply your trained LoRA.
// The weight (0.8 here) controls its intensity.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result is night and day. Because the model now has specific data about your facial structure, it can generate you realistically in any context you ask for. This is how you get consistency and realism that prompting alone can’t achieve.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A Word of Warning:&lt;/strong&gt; Be mindful of where you upload your photos. Using a local setup or a trusted, private cloud instance (like a Jupyter Notebook on Vertex AI) gives you full control. I’m hesitant to use free online services where I don’t know how my training data is being stored or used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Solution 3: The ‘Hybrid’ Option (AI Base, Photoshop Finish)
&lt;/h2&gt;

&lt;p&gt;Sometimes you get an image that’s 95% perfect. The composition is great, the lighting is on point, but the eyes look a little… off. Or the skin is just a bit too smooth. This is when you stop fighting the model and bring in other tools.&lt;/p&gt;

&lt;p&gt;This is a hacky but incredibly effective workflow we’ve used for one-off images:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generate the Base:&lt;/strong&gt; Use Solution 1 or 2 to get a strong base image. The overall structure should be good.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inpaint the Problem Areas:&lt;/strong&gt; In your image generator’s UI, use the “Inpaint” feature. Mask just the skin on the face, leaving the eyes, hair, and clothes alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run a Detail Pass:&lt;/strong&gt; Use a prompt like “ultra-detailed skin texture, pores, imperfections” with a very low denoising strength (0.2-0.35). The AI will only re-generate the masked area, adding the texture you asked for without changing the face.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Final Touches Elsewhere:&lt;/strong&gt; If that fails, don’t be afraid to take the image into Photoshop, GIMP, or Krita. Use frequency separation to add texture, or use the liquify tool to fix a slightly wonky eye. Combining the creative power of the AI with the precision of manual editing is often the fastest path to a perfect result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At the end of the day, these tools are just that—tools. Getting a great result isn’t about finding a magic prompt. It’s about understanding the “why” behind the waxy faces and using a combination of prompting, data, and workflow to force the machine to bend to reality, not the other way around.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nu" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Solved: Are any codeless test automation tools worth using?</title>
      <dc:creator>Darian Vance</dc:creator>
      <pubDate>Sun, 08 Mar 2026 17:36:23 +0000</pubDate>
      <link>https://vibe.forem.com/techresolve/solved-are-any-codeless-test-automation-tools-worth-using-46f5</link>
      <guid>https://vibe.forem.com/techresolve/solved-are-any-codeless-test-automation-tools-worth-using-46f5</guid>
      <description>&lt;h3&gt;
  
  
  🚀 Executive Summary
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Codeless test automation tools offer initial speed but often introduce long-term complexity and cost, struggling with intricate testing scenarios like external iframes or database validations. While useful for simple, tactical tasks, critical and complex testing demands a robust, code-based framework for scalable and maintainable quality assurance.&lt;/p&gt;

&lt;h4&gt;
  
  
  🎯 Key Takeaways
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Codeless tools are best suited for tactical, low-complexity tasks such as marketing site smoke tests, verifying staging deploys, or simple CRUD operations in stable admin panels.&lt;/li&gt;
&lt;li&gt;A hybrid approach is pragmatic, leveraging code-based frameworks (e.g., Playwright, Cypress) for critical, business-logic-heavy end-to-end tests and using codeless tools for supplementary, less critical UI regression checks.&lt;/li&gt;
&lt;li&gt;Treating test automation as a software development discipline with a code-first commitment provides ultimate flexibility, maintainability (e.g., Page Object Model), powerful debugging capabilities, and true CI/CD integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Codeless test automation tools promise speed but often hide long-term complexity and cost. A senior engineer breaks down when to use them for tactical wins versus when to commit to a code-based framework for scalable, maintainable testing.&lt;/p&gt;

&lt;h1&gt;
  
  
  The Codeless Conundrum: A Senior Engineer’s Take on ‘No-Code’ Test Automation
&lt;/h1&gt;

&lt;p&gt;I still remember the “Great QA Migration of ’21.” A new VP, enamored with a slick sales demo, mandated we move all our frontend testing to a new, flashy “codeless” platform. The first two months were magical. The QA team, with no coding experience, was creating dozens of tests a day. Management saw charts going up and to the right. Then reality hit. We needed to test a new two-factor authentication flow that involved interacting with an external iframe. The tool couldn’t do it. We needed to validate data written to our &lt;code&gt;prod-db-replica-01&lt;/code&gt; after a form submission. The tool couldn’t do it. We spent the next four months in a painful, expensive process of migrating everything *back* to our old Selenium framework, having lost nearly half a year of real progress. That’s why when I see Reddit threads asking if these tools are “worth it,” I feel a slight twitch in my eye.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why We Keep Having This Conversation
&lt;/h2&gt;

&lt;p&gt;Let’s be honest, the appeal is obvious. The promise of “democratizing” test automation is a powerful one. Management loves the idea of reducing reliance on expensive engineers, and manual testers feel empowered to contribute to the automation suite. The core drivers are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Need for Speed:&lt;/strong&gt; The pressure to ship features yesterday is immense. Codeless tools offer the illusion of instant productivity.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;– &lt;strong&gt;The Skills Gap:&lt;/strong&gt; Not every team has a dedicated Software Development Engineer in Test (SDET). Codeless tools seem to bridge this gap by allowing non-programmers to create tests.&lt;/p&gt;

&lt;p&gt;– &lt;strong&gt;Visual Simplicity:&lt;/strong&gt; A clean UI with drag-and-drop steps looks far less intimidating than an IDE filled with TypeScript or Python code.&lt;/p&gt;

&lt;p&gt;The problem is that these benefits are often front-loaded. The initial simplicity quickly gives way to a rigid, brittle system that breaks the moment your application does anything remotely complex. Testing isn’t just recording clicks; it’s a software development discipline in its own right.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Paths: How to Approach Automation in the Real World
&lt;/h2&gt;

&lt;p&gt;So, are they all useless? No. But you have to treat them like a specialized tool, not a silver bullet. Here’s how I see the landscape and how we actually use (or don’t use) them at TechResolve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: The Tactical Strike (The ‘Quick Fix’)
&lt;/h3&gt;

&lt;p&gt;This is where you use a codeless tool for a very specific, limited, and often non-critical purpose. It’s for tasks that are high-volume, repetitive, but low-complexity. Think of it as a scalpel, not a Swiss Army knife.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Marketing Site Smoke Tests:&lt;/strong&gt; Is the homepage loading? Does the “Request a Demo” form submit? Perfect. This doesn’t need to run in a full CI/CD pipeline and can be owned by the marketing team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;– &lt;strong&gt;Verifying Staging Deploys:&lt;/strong&gt; A simple, automated “site is up” test that runs after a deploy to &lt;code&gt;staging-webapp-04&lt;/code&gt; can be a quick win.&lt;/p&gt;

&lt;p&gt;– &lt;strong&gt;Simple CRUD in an Admin Panel:&lt;/strong&gt; You have a back-office tool where you just need to confirm you can create, read, update, and delete a user. If the UI is stable, a codeless tool can handle this just fine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The moment you find yourself trying to “trick” the tool with custom JavaScript injections or fighting its selector logic, you’ve crossed the line. This is a sign that you’re using the wrong tool for the job. Stop immediately and move to a real code-based solution.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Approach 2: The Hybrid Coexistence (The ‘Permanent Fix’)
&lt;/h3&gt;

&lt;p&gt;This is probably the most realistic and pragmatic approach for many large organizations. You don’t have to choose one or the other. You can create a tiered testing strategy where different tools are used for different purposes.&lt;/p&gt;

&lt;p&gt;The core principle is this: &lt;strong&gt;Your critical, business-logic-heavy, end-to-end tests MUST live in a robust, code-based framework.&lt;/strong&gt; This is your source of truth. It’s version-controlled in Git, it runs in your CI pipeline on every commit to the &lt;code&gt;main&lt;/code&gt; branch, and it’s maintained by engineers.&lt;/p&gt;

&lt;p&gt;The codeless tools are then used as a supplementary layer, often by manual QA or business analysts, to cover happy paths or minor UI regression tests. The key is that a failure in the codeless suite is treated as a “warning,” while a failure in the core code-based suite is a “blocker” that stops a release.&lt;/p&gt;

&lt;p&gt;At TechResolve, our Playwright suite tests the entire user journey from signup to payment processing. But our QA team uses a recorder tool to quickly check for UI bugs on less critical pages, like our “About Us” or “Careers” sections, after a CMS update.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 3: The Code-First Commitment (The ‘Nuclear’ Option)
&lt;/h3&gt;

&lt;p&gt;This is my preferred approach, and the one I advocate for on any serious project. Accept that test automation *is* software development. Treat your test suite with the same respect you treat your application code. This means choosing a modern, powerful framework like Playwright, Cypress, or a Selenium/WebDriverIO setup and investing the time to build it right.&lt;/p&gt;

&lt;p&gt;The upfront cost is higher. It requires engineers who can write clean, maintainable code. But the long-term payoff is massive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ultimate Flexibility:&lt;/strong&gt; Need to make an API call to seed data, then verify a WebSocket message, then check a value in a database? No problem. You have the full power of a programming language at your disposal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;– &lt;strong&gt;Maintainability:&lt;/strong&gt; Using patterns like the Page Object Model (POM) makes your tests readable and easy to update when the UI changes.&lt;/p&gt;

&lt;p&gt;– &lt;strong&gt;Powerful Debugging:&lt;/strong&gt; You get step-through debugging, trace viewers, and detailed logs—not just a failed step in a black-box UI.&lt;/p&gt;

&lt;p&gt;– &lt;strong&gt;True CI/CD Integration:&lt;/strong&gt; Your tests are a native part of your pipeline, not a disconnected third-party service you have to awkwardly trigger via webhooks.&lt;/p&gt;

&lt;p&gt;Don’t be intimidated by “code.” A good test is often simpler and more explicit than a list of recorded steps. Here’s a dead-simple Playwright test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { test, expect } from '@playwright/test';

test('should allow a user to log in successfully', async ({ page }) =&amp;gt; {
  // 1. Go to the login page
  await page.goto('https://app.techresolve.com/login');

  // 2. Fill in credentials
  await page.getByLabel('Email').fill('darian.vance@techresolve.com');
  await page.getByLabel('Password').fill('S3cureP@ssw0rd!');

  // 3. Click login button
  await page.getByRole('button', { name: 'Log In' }).click();

  // 4. Verify we landed on the dashboard
  await expect(page.locator('h1')).toHaveText('Welcome, Darian!');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. It’s readable, self-documenting, and infinitely more powerful than a recorder.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison at a Glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Approach 1: Tactical Strike&lt;/th&gt;
&lt;th&gt;Approach 2: Hybrid&lt;/th&gt;
&lt;th&gt;Approach 3: Code-First&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed to Start&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Extremely Fast&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;Slowest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Long-Term Maintainability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;Moderate (if managed well)&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flexibility &amp;amp; Power&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Very Low&lt;/td&gt;
&lt;td&gt;Low to Moderate&lt;/td&gt;
&lt;td&gt;Extremely High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Required Skillset&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Non-technical&lt;/td&gt;
&lt;td&gt;Mixed (QA &amp;amp; SDET)&lt;/td&gt;
&lt;td&gt;Engineering / SDET&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  My Final Take
&lt;/h2&gt;

&lt;p&gt;Codeless tools have a place. They are a single tool in a much larger toolbox. Using them for quick-and-dirty validation of simple, stable UIs is a perfectly valid strategy. But relying on them as the foundation of your quality strategy for a complex, evolving application is an expensive mistake waiting to happen. You’ll eventually hit a wall, and the cost of migrating off the platform will negate all the initial time you saved.&lt;/p&gt;

&lt;p&gt;Invest in your people, treat testing as a core engineering discipline, and build a robust, code-based foundation. It’s the only way to achieve sustainable quality and velocity at scale.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnwjgilechjb8hqoqlrg1.png" alt="Darian Vance" width="758" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://wp.me/pbK4oa-nt" rel="noopener noreferrer"&gt;Read the original article on TechResolve.blog&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;☕ &lt;strong&gt;Support my work&lt;/strong&gt;  &lt;/p&gt;

&lt;p&gt;If this article helped you, you can buy me a coffee:  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://buymeacoffee.com/darianvance" rel="noopener noreferrer"&gt;https://buymeacoffee.com/darianvance&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>cloud</category>
    </item>
  </channel>
</rss>
