<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: &lt;devtips/&gt;</title>
    <description>The latest articles on Forem by &lt;devtips/&gt; (@dev_tips).</description>
    <link>https://forem.com/dev_tips</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%2F2901662%2F1dcce5de-7920-43a0-a337-e1dfb375b204.png</url>
      <title>Forem: &lt;devtips/&gt;</title>
      <link>https://forem.com/dev_tips</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dev_tips"/>
    <language>en</language>
    <item>
      <title>30+ Linux environment variables hackers memorize on day one (and most devs never bother with</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Thu, 07 May 2026 14:08:40 +0000</pubDate>
      <link>https://forem.com/dev_tips/30-linux-environment-variables-hackers-memorize-on-day-one-and-most-devs-never-bother-with-ak</link>
      <guid>https://forem.com/dev_tips/30-linux-environment-variables-hackers-memorize-on-day-one-and-most-devs-never-bother-with-ak</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="8aaa"&gt;
&lt;br&gt;
&lt;em&gt;Alt:&lt;/em&gt; “Your Linux setup is leaking. These 30+ environment variables are why.”&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p id="2fd7"&gt;You’ve been writing code on Linux for years. Maybe you run it in Docker, on a VPS, on your actual machine like a person with good taste. You know your way around a terminal. You feel comfortable there.&lt;/p&gt;
&lt;p id="0d2e"&gt;And yet.&lt;/p&gt;
&lt;p id="60fb"&gt;Somewhere between your first &lt;code&gt;apt install&lt;/code&gt; and your current career, you quietly decided that environment variables were a "good to know someday" topic. You learned PATH. You learned HOME. You maybe, on a heroic day, looked up what SHELL does. Then you moved on.&lt;/p&gt;
&lt;p id="f6e2"&gt;That’s fine. Until you’re debugging a production issue at an hour you’d rather not name, and the answer is buried in a variable you’ve never heard of. Or worse until someone who has done their homework walks into a system and does things you can’t explain because you never learned the control plane sitting right under your nose.&lt;/p&gt;
&lt;p id="f0c8"&gt;Here’s the uncomfortable truth: environment variables aren’t a Linux curiosity. They’re the configuration layer for every process, every shell session, every tool you run. Hackers the ethical, CTF-grinding, red-team-report-writing kind treat them like first-class knowledge. Most developers treat them like a footnote.&lt;/p&gt;
&lt;p id="4d3e"&gt;This article fixes that. We’re going through 30+ of them across three tiers: the essentials you should already know cold, the power-user variables most people skip entirely, and the ones that show up in every serious security engagement. By the end, you’ll have a mental model for this stuff that actually sticks.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="618a"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; There are dozens of Linux environment variables that shape how your system behaves. Most developers know three. This guide covers 30+, split into essentials, power-user config, and security-critical variables with real examples and the context to actually use them.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="4823"&gt;Tier 1: The essentials the 10 you should know cold&lt;/h2&gt;
&lt;p id="d24f"&gt;Most developers exist in a comfortable relationship with about three environment variables. PATH gets them where they need to go. HOME tells their tools where to store things. USER shows up occasionally in a script. That’s the whole map.&lt;/p&gt;
&lt;p id="cf5e"&gt;The problem is that “knowing” PATH and actually understanding what it does are two very different things. And when something breaks a command not found, a tool opening the wrong editor, logs full of encoding garbage the answer is almost always in one of these ten variables. You just didn’t know to look there.&lt;/p&gt;
&lt;p id="83c6"&gt;Let’s close that gap.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="0cba"&gt;&lt;strong&gt;PATH&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="25fb"&gt;The most consequential variable on your system. When you type any command, Linux doesn’t search every directory it walks through PATH left to right and stops at the first match.&lt;/p&gt;
&lt;pre&gt;&lt;span id="ddb9"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$PATH&lt;/span&gt;&lt;br&gt;&lt;span&gt;# /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; PATH=/usr/local/bin:&lt;span&gt;$PATH&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="628e"&gt;Order matters more than most people realize. If you’ve ever installed a tool and gotten an older version back, PATH ordering is your suspect. The directory that appears first wins, every time.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="3505"&gt;&lt;strong&gt;HOME&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="2549"&gt;The absolute path to your current user’s home directory. Nearly every application uses this to figure out where to read config, write cache, and store data.&lt;/p&gt;
&lt;pre&gt;&lt;span id="c312"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$HOME&lt;/span&gt;&lt;br&gt;&lt;span&gt;# /home/devtips&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="d76b"&gt;Change HOME and you change where everything lands. Useful to know when you’re scripting something that needs to behave differently across users.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AV_ZBMy37qOd1F1lvIWie0w.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="b376"&gt;&lt;strong&gt;USER&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="a0f4"&gt;The username of whoever is running the current session. Simple on the surface, essential in any script that needs to branch based on who’s executing it.&lt;/p&gt;
&lt;pre&gt;&lt;span id="2e09"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$USER&lt;/span&gt;&lt;br&gt;&lt;span&gt;# devtips&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f4b6"&gt;Don’t hardcode usernames in scripts. Read USER instead. Your future self will thank you when someone else runs it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="810e"&gt;&lt;strong&gt;SHELL&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="b664"&gt;The path to your active shell binary. This determines your scripting behavior, your tab completion, and your default prompt.&lt;/p&gt;
&lt;pre&gt;&lt;span id="cb56"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$SHELL&lt;/span&gt;&lt;br&gt;&lt;span&gt;# /bin/bash&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="31f5"&gt;Never assume bash. In a lot of modern setups especially containers and minimal server installs you’re on dash, sh, or zsh. SHELL tells you what you’re actually dealing with.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="37bb"&gt;&lt;strong&gt;PWD&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="3f58"&gt;Your current working directory, updated automatically every time you &lt;code&gt;cd&lt;/code&gt;. More useful in scripts than in interactive use.&lt;/p&gt;
&lt;pre&gt;&lt;span id="5be5"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$PWD&lt;/span&gt;&lt;br&gt;&lt;span&gt;# /home/devtips/projects&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="1207"&gt;Use it to build absolute paths inside scripts instead of relying on relative paths that break the moment someone runs the script from a different directory.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="60ba"&gt;&lt;strong&gt;HOSTNAME&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="2008"&gt;The network name of the machine you’re on. The one you actually want in scripts that need to behave differently across dev, staging, and prod.&lt;/p&gt;
&lt;pre&gt;&lt;span id="902d"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$HOSTNAME&lt;/span&gt;&lt;br&gt;&lt;span&gt;# ubuntu-server&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="1220"&gt;I’ve seen scripts that hardcode machine names. I’ve seen what happens when those machines get renamed. Set HOSTNAME checks in your scripts and stop praying.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="8d46"&gt;&lt;strong&gt;LANG&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="b10e"&gt;Controls language, character encoding, and locale behavior across the entire system.&lt;/p&gt;
&lt;pre&gt;&lt;span id="a9a0"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$LANG&lt;/span&gt;&lt;br&gt;&lt;span&gt;# en_US.UTF-8&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; LANG=en_US.UTF-8&lt;/span&gt;&lt;/pre&gt;
&lt;p id="e36a"&gt;Mismatched locales are responsible for some of the most baffling, hard-to-reproduce bugs in production. Logs showing up as question marks. Sorting behaving wrong. String comparisons failing in ways that make no sense. Always set this explicitly in production scripts don’t inherit whatever the system happens to have.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="75c0"&gt;&lt;strong&gt;TERM&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="9055"&gt;Tells applications what kind of terminal they’re dealing with, which determines what escape codes they use for colors, cursor movement, and formatting.&lt;/p&gt;
&lt;pre&gt;&lt;span id="4763"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$TERM&lt;/span&gt;&lt;br&gt;&lt;span&gt;# xterm-256color&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="feda"&gt;If your terminal output looks garbled, colors aren’t rendering, or a tool is behaving like it’s running blind TERM is your first check. A lot of SSH sessions inherit a wrong TERM value and everything downstream breaks quietly.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="1248"&gt;&lt;strong&gt;EDITOR&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="8be7"&gt;Defines which text editor programs open when they need you to write something git commit messages, crontab entries, anything that spawns an interactive editor.&lt;/p&gt;
&lt;pre&gt;&lt;span id="2a09"&gt;&lt;span&gt;export&lt;/span&gt; EDITOR=vim&lt;/span&gt;&lt;/pre&gt;
&lt;p id="949c"&gt;Set this once in your &lt;code&gt;~/.bashrc&lt;/code&gt; and every tool that respects it falls in line. Not setting it means you're at the mercy of whatever the system default is. On a lot of servers, that's ed. You do not want to meet ed unprepared.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="fab0"&gt;&lt;strong&gt;TZ&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="d3d1"&gt;Sets the timezone for the current session and any process that inherits the environment.&lt;/p&gt;
&lt;pre&gt;&lt;span id="d2c1"&gt;&lt;span&gt;export&lt;/span&gt; TZ=America/New_York&lt;br&gt;&lt;span&gt;# or&lt;/span&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; TZ=Asia/Karachi&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8e08"&gt;This one bites teams constantly. Two servers, same codebase, logs showing timestamps an hour apart someone forgot to standardize TZ across environments. In containerized systems especially, never assume the timezone is what you think it is. Set it explicitly and move on.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="d617"&gt;Those are your ten. If you can explain what each one does without looking it up, you’re already ahead of most people running Linux daily. If two or three were new good, now they’re not.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="4a93"&gt;Tier 2: Power user variables most devs sleep on&lt;/h2&gt;
&lt;p id="5543"&gt;Here’s where the gap actually opens up. Tier 1 variables are the ones you stumble into eventually they show up in error messages, Stack Overflow answers, and “getting started with Linux” guides. You learn them by accident.&lt;/p&gt;
&lt;p id="b2ab"&gt;Tier 2 doesn’t work that way. Nobody’s going to hand you these. They live in the corners of documentation pages, in experienced engineers’ dotfiles, in the kind of config that makes you think “wait, you can just &lt;em&gt;do&lt;/em&gt; that?” the first time you see it. Most developers go years without touching them. Power users configure them on day one.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="b724"&gt;&lt;strong&gt;PS1&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="4c77"&gt;Your bash prompt is programmable. PS1 is the variable that controls exactly what it displays.&lt;/p&gt;
&lt;pre&gt;&lt;span id="04b6"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$PS1&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; PS1=&lt;span&gt;"[\u@\h \W]\$ "&lt;/span&gt;&lt;br&gt;&lt;span&gt;# Output: [devtips@ubuntu projects]$&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5a3e"&gt;&lt;code&gt;\u&lt;/code&gt; is your username, &lt;code&gt;\h&lt;/code&gt; is hostname, &lt;code&gt;\W&lt;/code&gt; is the current directory. You can layer in colors, git branch names, exit codes from the last command, timestamps basically anything. Think of the default prompt like a stock game HUD. PS1 is where you remap everything to actually make sense for how you work. If you're still on the default, you're leaving information on the table every time you open a terminal.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;p id="6c55"&gt;&lt;strong&gt;HISTSIZE&lt;/strong&gt;&lt;/p&gt;
&lt;p id="0eed"&gt;Controls how many commands are kept in memory during your active session.&lt;/p&gt;
&lt;p id="1327"&gt;bash&lt;/p&gt;
&lt;pre&gt;&lt;span id="b936"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$HISTSIZE&lt;/span&gt;&lt;br&gt;&lt;span&gt;# 1000&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; HISTSIZE=10000&lt;/span&gt;&lt;/pre&gt;
&lt;p id="1abc"&gt;The default on most systems is 1000. That sounds like a lot until you’re trying to find a command you ran three days ago and it’s gone. Power users set this to something large ten thousand, fifty thousand and treat their history like a searchable log of everything they’ve done. Pair it with &lt;code&gt;Ctrl+R&lt;/code&gt; for reverse history search and you've got a lightweight audit trail of your own work.&lt;/p&gt;
&lt;p id="ed00"&gt;We’ll revisit HISTSIZE in Tier 3 for very different reasons.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="15f1"&gt;&lt;strong&gt;HISTFILESIZE&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="78b9"&gt;The on-disk companion to HISTSIZE. Controls how many commands get saved to &lt;code&gt;~/.bash_history&lt;/code&gt; when your session ends.&lt;/p&gt;
&lt;p id="328e"&gt;bash&lt;/p&gt;
&lt;pre&gt;&lt;span id="cbf3"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$HISTFILESIZE&lt;/span&gt;&lt;br&gt;&lt;span&gt;# 2000&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; HISTFILESIZE=20000&lt;/span&gt;&lt;/pre&gt;
&lt;p id="cb91"&gt;These two variables are separate knobs and that trips people up. HISTSIZE is what’s held in memory during your session. HISTFILESIZE is what gets written to disk afterward. You can have a large in-session history and a small file, or vice versa. Set both deliberately instead of inheriting whatever your distro shipped with.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AJ_9-lX8zKdxZEg-jDRqs8Q.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="1e1a"&gt;&lt;strong&gt;MANPATH&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="a514"&gt;Defines where the &lt;code&gt;man&lt;/code&gt; command looks for manual pages.&lt;/p&gt;
&lt;pre&gt;&lt;span id="d375"&gt;&lt;span&gt;export&lt;/span&gt; MANPATH=/usr/local/share/man:&lt;span&gt;$MANPATH&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="c604"&gt;This one matters the moment you start installing tools to non-standard locations custom builds, things in &lt;code&gt;/opt&lt;/code&gt;, tools you've compiled yourself. If &lt;code&gt;man yourtool&lt;/code&gt; returns nothing, the manual exists somewhere your system doesn't know to look. Add the right path to MANPATH and it works immediately. Most people just Google the man page instead. Setting MANPATH is faster and works offline.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="1595"&gt;&lt;strong&gt;DISPLAY&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="a467"&gt;Used by the X Window System to specify which display server to connect to.&lt;/p&gt;
&lt;pre&gt;&lt;span id="65d3"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$DISPLAY&lt;/span&gt;&lt;br&gt;&lt;span&gt;# :0.0&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; DISPLAY=:0.0&lt;/span&gt;&lt;/pre&gt;
&lt;p id="ff59"&gt;You won’t think about this variable until the exact moment you need it. That moment is usually: you SSH into a remote machine, try to open something with a GUI, and get a cryptic error about not being able to connect to a display. DISPLAY is what tells the application where to render. Get it right and the GUI opens on your local screen. Get it wrong and you’re reading X11 error messages at a time of day that tests your patience. Enable X11 forwarding in your SSH config and set DISPLAY accordingly.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="d466"&gt;&lt;strong&gt;MAIL&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="7d0e"&gt;Points to the location of the current user’s mail spool where local system mail lands.&lt;/p&gt;
&lt;pre&gt;&lt;span id="42c6"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$MAIL&lt;/span&gt;&lt;br&gt;&lt;span&gt;# /var/mail/devlink&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="dcaa"&gt;Nobody thinks about this one. Cron jobs do. When a scheduled task fails or produces output, it doesn’t throw an error into the void it sends local mail. If MAIL isn’t set or nobody’s checking it, those failure messages have been quietly piling up unseen. This is genuinely how some cron jobs silently die for months before anyone notices. Check your mail spool occasionally. You might find a graveyard.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="5ea2"&gt;&lt;strong&gt;OSTYPE&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="d96c"&gt;Tells you what operating system you’re running on at the shell level.&lt;/p&gt;
&lt;pre&gt;&lt;span id="3313"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$OSTYPE&lt;/span&gt;&lt;br&gt;&lt;span&gt;# linux-gnu&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="555e"&gt;The place this earns its keep is cross-platform shell scripting. If you’re writing a script that needs to run on both Linux and macOS and eventually you will be OSTYPE lets you branch cleanly without spawning a &lt;code&gt;uname&lt;/code&gt; subprocess. Linux returns &lt;code&gt;linux-gnu&lt;/code&gt;, macOS returns &lt;code&gt;darwin&lt;/code&gt;, BSD variants have their own values. One variable, clean conditional logic, no forks.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="8611"&gt;&lt;strong&gt;COLORTERM&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="04bd"&gt;Signals to applications that your terminal supports true color full 24-bit RGB rather than the 256-color or 8-color fallback modes.&lt;/p&gt;
&lt;pre&gt;&lt;span id="3d64"&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;$COLORTERM&lt;/span&gt;&lt;br&gt;&lt;span&gt;# truecolor&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; COLORTERM=truecolor&lt;/span&gt;&lt;/pre&gt;
&lt;p id="226c"&gt;This is one of those variables where not setting it correctly causes problems that look like something completely different. Your terminal supports full color. Your tool supports full color. But COLORTERM isn’t set, so the tool falls back to 256 colors and everything looks slightly off in a way you can’t quite name. Set it explicitly in your dotfiles and stop wondering why your color scheme looks duller on one machine than another.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="dd4a"&gt;Those eight variables separate the people who use Linux from the people who actually configure it. None of them are secrets they’re all in the documentation. But documentation doesn’t tell you &lt;em&gt;why&lt;/em&gt; they matter or when you’ll need them. That’s what experience does, and now you’ve got a head start.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="d0eb"&gt;Tier 3: The hacker toolkit&lt;/h2&gt;
&lt;blockquote&gt;&lt;p id="da52"&gt;&lt;strong&gt;&lt;em&gt;Disclaimer:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Everything in this section is for ethical hacking, penetration testing, CTF challenges, and defensive security awareness. Understanding what attackers use is how defenders build better detection. Use these only on systems you own or have explicit written permission to test.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p id="a7b2"&gt;This is where the article gets interesting.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="e674"&gt;Tiers 1 and 2 are about configuration. Tier 3 is about control. These variables don’t just shape how your shell looks or which directories get searched they touch process loading, traffic routing, credential handling, library injection, and session forensics. They’re the reason experienced security engineers audit environment variables on any box they’re responsible for, and why attackers learn them before almost anything else.&lt;/p&gt;
&lt;p id="a0c3"&gt;None of this is exotic. It’s all documented. It’s all standard Linux. That’s precisely what makes it dangerous.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2ACZYYvWbxuBVtptM8qN0kug.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="1d51"&gt;&lt;strong&gt;HISTSIZE=0 and HISTFILESIZE=0&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="9f92"&gt;&lt;span&gt;export&lt;/span&gt; HISTSIZE=0&lt;br&gt;&lt;span&gt;export&lt;/span&gt; HISTFILESIZE=0&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8d16"&gt;Set both to zero at the start of a session and you get complete command history suppression. Nothing gets stored in memory, nothing gets written to disk when the session ends. Standard OPSEC in any authorized red team engagement.&lt;/p&gt;
&lt;p id="0fbe"&gt;Why defenders care: if you’re doing incident response on a compromised machine and find these set early in a session especially in &lt;code&gt;.bashrc&lt;/code&gt; or &lt;code&gt;/etc/profile&lt;/code&gt; someone was thinking about logging before they started working. That's not accidental configuration. That's a signal.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="2e9b"&gt;&lt;strong&gt;http_proxy and https_proxy&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="f88c"&gt;&lt;span&gt;export&lt;/span&gt; http_proxy=&lt;span&gt;"&lt;a href="http://10.10.10.10:8080" rel="noopener noreferrer"&gt;http://10.10.10.10:8080&lt;/a&gt;"&lt;/span&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; https_proxy=&lt;span&gt;"&lt;a href="http://10.10.10.10:8080" rel="noopener noreferrer"&gt;http://10.10.10.10:8080&lt;/a&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="a2f6"&gt;Any process that respects these variables routes its traffic through the specified proxy. This is how security testers intercept application traffic through tools like &lt;a href="https://portswigger.net/burp" rel="noopener ugc nofollow noreferrer"&gt;Burp Suite&lt;/a&gt; during an engagement without touching application code, without modifying config files, without restarting services. Set the variable, run the process, read the traffic.&lt;/p&gt;
&lt;p id="d630"&gt;The lowercase versions (&lt;code&gt;http_proxy&lt;/code&gt;) are respected by most command-line tools. Some applications only check the uppercase versions. In practice, set both.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="9128"&gt;&lt;strong&gt;SSL_CERT_FILE and SSL_CERT_DIR&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="1e57"&gt;&lt;span&gt;export&lt;/span&gt; SSL_CERT_FILE=/path/to/ca-bundle.pem&lt;br&gt;&lt;span&gt;export&lt;/span&gt; SSL_CERT_DIR=/path/to/ca-certificates&lt;/span&gt;&lt;/pre&gt;
&lt;p id="e45f"&gt;Processes that read these variables trust the certificates you point them at. In an authorized testing context, this is how you get an application to trust your proxy’s self-signed certificate so you can inspect encrypted HTTPS traffic without the tool throwing certificate errors and refusing to connect.&lt;/p&gt;
&lt;p id="a792"&gt;Combined with &lt;code&gt;http_proxy&lt;/code&gt;, these two variables give you a complete traffic interception setup without touching a single config file inside the application.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="13b2"&gt;&lt;strong&gt;LD_PRELOAD&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="127a"&gt;&lt;span&gt;export&lt;/span&gt; LD_PRELOAD=/tmp/custom.so&lt;/span&gt;&lt;/pre&gt;
&lt;p id="a8f4"&gt;This is the most powerful variable on this list. When set, the dynamic linker loads your specified shared library &lt;em&gt;before anything else&lt;/em&gt; before the C standard library, before every other dependency the binary has. Functions in your library override functions in any other library the process loads.&lt;/p&gt;
&lt;p id="47fd"&gt;The implication: you can intercept system calls, hook functions, and fundamentally alter the behavior of a running process entirely from outside its source code. No recompilation. No patching. Just an environment variable.&lt;/p&gt;
&lt;p id="0e31"&gt;In CTF challenges and authorized penetration tests, LD_PRELOAD shows up in privilege escalation paths, sandbox bypasses, and function hooking scenarios. It’s also widely used legitimately memory profilers, debugging tools, and performance analyzers all use this same mechanism.&lt;/p&gt;
&lt;p id="a46b"&gt;Why defenders care: monitor for LD_PRELOAD pointing to paths in &lt;code&gt;/tmp&lt;/code&gt; or world-writable directories. Legitimate tools don't load libraries from there. Something else does.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="b1d5"&gt;&lt;strong&gt;LD_LIBRARY_PATH&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="c584"&gt;&lt;span&gt;export&lt;/span&gt; LD_LIBRARY_PATH=/tmp/mylib:&lt;span&gt;$LD_LIBRARY_PATH&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f097"&gt;Defines the directories the dynamic linker searches for shared libraries. By prepending a custom directory, you can substitute your own version of any library a binary depends on the binary loads your library instead of the real one and never knows the difference.&lt;/p&gt;
&lt;p id="2586"&gt;This is the environment variable equivalent of DLL hijacking on Windows. Same concept, same impact, different operating system. The attack works because most binaries trust that the libraries they load are the ones they expect.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="60bd"&gt;&lt;strong&gt;SUDO_ASKPASS&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="3b69"&gt;&lt;span&gt;export&lt;/span&gt; SUDO_ASKPASS=/tmp/fake-prompt&lt;br&gt;sudo -A &lt;span&gt;whoami&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="6288"&gt;When &lt;code&gt;sudo&lt;/code&gt; is called with the &lt;code&gt;-A&lt;/code&gt; flag, instead of prompting for a password in the terminal, it executes whatever program is set in SUDO_ASKPASS and uses that program's output as the password. In authorized social engineering simulations, this technique demonstrates how applications and GUI wrappers around sudo can be leveraged to intercept credential input without the user realizing the prompt they're responding to isn't the real one.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="ef3d"&gt;&lt;strong&gt;LD_DEBUG&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="4e43"&gt;&lt;span&gt;export&lt;/span&gt; LD_DEBUG=libs&lt;br&gt;./somebinary&lt;/span&gt;&lt;/pre&gt;
&lt;p id="e642"&gt;A reconnaissance variable. Setting it makes the dynamic linker print detailed output about every library being loaded the full path, the search order, which directories were checked, which version was found. No special permissions required. No tools to install.&lt;/p&gt;
&lt;p id="12df"&gt;In authorized engagements, this is how you identify which libraries a binary depends on and whether any of them are loaded from locations that could be hijacked with LD_LIBRARY_PATH. It turns library loading from a black box into a visible, auditable process.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2APk1zxjt62iCMCnFFAUGXbQ.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="6bc5"&gt;&lt;strong&gt;IFS&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="2c8b"&gt;&lt;span&gt;export&lt;/span&gt; IFS=$&lt;span&gt;'\n'&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5fb7"&gt;IFS Internal Field Separator tells bash how to split strings into tokens. The default is space, tab, and newline. Changing it changes how every command and script in your session parses its inputs.&lt;/p&gt;
&lt;p id="9742"&gt;In exploit development and CTF challenges, subtle IFS manipulation breaks input validation in scripts that weren’t written with this in mind. A script that sanitizes space-separated input suddenly behaves differently when the separator changes. It’s a small variable with outsized consequences in poorly written shell code which, in production environments, is not rare.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="6d0f"&gt;&lt;strong&gt;GDBINIT&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="dd46"&gt;&lt;span&gt;export&lt;/span&gt; GDBINIT=/tmp/custom-gdbinit&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8572"&gt;GDB the GNU Debugger reads initialization commands from the file specified here on startup. In authorized assessments, this demonstrates how developer tooling itself can become an execution vector when environment variables aren’t controlled. CI pipelines, developer workstations, build servers anything that invokes GDB in an environment where GDBINIT can be influenced is a potential target.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="c928"&gt;&lt;strong&gt;TMOUT&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="5deb"&gt;&lt;span&gt;export&lt;/span&gt; TMOUT=1&lt;/span&gt;&lt;/pre&gt;
&lt;p id="60f3"&gt;Sets bash to automatically terminate after the specified number of seconds of inactivity. Setting it to 1 closes a shell almost immediately after it goes idle. In an authorized engagement context, this is how you ensure a session closes cleanly without leaving an open shell exposed on a system you’re no longer actively using.&lt;/p&gt;
&lt;p id="8fd2"&gt;For defenders, it’s also worth setting in &lt;code&gt;/etc/profile&lt;/code&gt; on any server where unattended sessions are a risk. Idle shells with elevated privileges sitting open are an opportunity nobody needs to create.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;p id="789e"&gt;&lt;strong&gt;XDG_CONFIG_HOME&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="a525"&gt;&lt;span&gt;export&lt;/span&gt; XDG_CONFIG_HOME=/tmp/custom-config&lt;/span&gt;&lt;/pre&gt;
&lt;p id="3394"&gt;Applications that follow the &lt;a href="https://specifications.freedesktop.org/basedir-spec/latest/" rel="noopener ugc nofollow noreferrer"&gt;XDG Base Directory Specification&lt;/a&gt; read their config from this path. Redirect it to a controlled directory and you supply a completely custom configuration to any XDG-compliant application without modifying a single file on the real filesystem. Clean, contained, reversible.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="5dc9"&gt;Thirteen variables. Every one of them is in the man pages, in the official documentation, available to anyone who reads carefully enough. The difference isn’t access to secret knowledge it’s whether you took the time to understand what was already there.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="73a9"&gt;How to audit and manage your environment properly&lt;/h2&gt;
&lt;blockquote&gt;&lt;p id="3093"&gt;You’ve now got 30+ variables in your head. The natural next question is: what’s actually set on my system right now, and how do I control it properly?&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="5208"&gt;Most people interact with environment variables reactively they set something when a tool breaks, forget where they set it, and spend twenty minutes debugging the wrong file six months later. The fix is understanding the three distinct layers your environment is built from, and having a handful of commands you can reach for without thinking.&lt;/p&gt;
&lt;h3 id="5429"&gt;&lt;strong&gt;Viewing what’s currently set&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="6e68"&gt;&lt;strong&gt;Four commands, four slightly different outputs:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="14bd"&gt;&lt;span&gt;# Everything exported to the current environment&lt;/span&gt;&lt;br&gt;&lt;span&gt;printenv&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# A specific variable&lt;/span&gt;&lt;br&gt;&lt;span&gt;printenv&lt;/span&gt; PATH&lt;br&gt;&lt;br&gt;&lt;span&gt;# All exported variables (similar to printenv)&lt;/span&gt;&lt;br&gt;&lt;span&gt;env&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# All shell variables including unexported ones - verbose&lt;/span&gt;&lt;br&gt;&lt;span&gt;set&lt;/span&gt; | less&lt;/span&gt;&lt;/pre&gt;
&lt;p id="261e"&gt;&lt;code&gt;printenv&lt;/code&gt; and &lt;code&gt;env&lt;/code&gt; show you what's exported — what child processes will inherit. &lt;code&gt;set&lt;/code&gt; shows everything including shell-local variables that don't get passed down. For most auditing purposes, &lt;code&gt;printenv&lt;/code&gt; is what you want. For thoroughness, &lt;code&gt;set | less&lt;/code&gt; and scroll.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="b33d"&gt;&lt;strong&gt;Setting variables know your layers&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="6224"&gt;This is where most confusion lives. There are three distinct scopes and they don’t interact the way people assume:&lt;/p&gt;
&lt;pre&gt;&lt;span id="0185"&gt;&lt;span&gt;# Current session only — gone when you close the terminal&lt;/span&gt;&lt;br&gt;&lt;span&gt;export&lt;/span&gt; MY_VAR=&lt;span&gt;"value"&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# Permanent for your user - survives reboots, applies to new sessions&lt;/span&gt;&lt;br&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;'export MY_VAR="value"'&lt;/span&gt; &amp;gt;&amp;gt; ~/.bashrc&lt;br&gt;&lt;span&gt;source&lt;/span&gt; ~/.bashrc&lt;br&gt;&lt;span&gt;# System-wide for all users - requires root&lt;/span&gt;&lt;br&gt;&lt;span&gt;echo&lt;/span&gt; &lt;span&gt;'MY_VAR="value"'&lt;/span&gt; &amp;gt;&amp;gt; /etc/environment&lt;/span&gt;&lt;/pre&gt;
&lt;p id="c330"&gt;The hierarchy goes: system (&lt;code&gt;/etc/environment&lt;/code&gt;) → user shell config (&lt;code&gt;~/.bashrc&lt;/code&gt;, &lt;code&gt;~/.bash_profile&lt;/code&gt;) → current session (&lt;code&gt;export&lt;/code&gt;). Each layer can override the one above it. When a variable is behaving unexpectedly, you're almost always looking at a conflict between two of these layers something set in &lt;code&gt;/etc/environment&lt;/code&gt; getting overridden in &lt;code&gt;.bashrc&lt;/code&gt;, or a session export shadowing both.&lt;/p&gt;
&lt;p id="4644"&gt;I’ve personally spent an embarrassing amount of time debugging a “why is this variable always wrong” issue that turned out to be set correctly in &lt;code&gt;.bashrc&lt;/code&gt;, overridden in &lt;code&gt;.bash_profile&lt;/code&gt;, and then overridden again by a script that exported it fresh on every run. Check all three layers before assuming the system is broken.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;p id="dc26"&gt;&lt;strong&gt;Removing and locking variables&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="041d"&gt;&lt;span&gt;# Remove a variable from the current session&lt;/span&gt;&lt;br&gt;&lt;span&gt;unset&lt;/span&gt; MY_VAR&lt;br&gt;&lt;br&gt;&lt;span&gt;# Lock a variable so it can't be modified in the current session&lt;/span&gt;&lt;br&gt;&lt;span&gt;readonly&lt;/span&gt; SECURE_VAR=&lt;span&gt;"value"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="aa25"&gt;&lt;code&gt;unset&lt;/code&gt; is straightforward. &lt;code&gt;readonly&lt;/code&gt; is underused once set, any attempt to modify or unset that variable in the current session returns an error. Useful for variables that should never change after initialization, like paths to critical binaries in a script.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="a20d"&gt;&lt;strong&gt;Passing variables to a single command without exporting&lt;/strong&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;span id="4afb"&gt;MY_VAR=&lt;span&gt;"value"&lt;/span&gt; some-command&lt;/span&gt;&lt;/pre&gt;
&lt;p id="3f01"&gt;This sets the variable in the environment of &lt;code&gt;some-command&lt;/code&gt; only it doesn't persist to your shell, doesn't affect other processes, disappears immediately after the command finishes. Useful for one-off overrides without polluting your session. A lot of developers don't know this syntax exists and reach for &lt;code&gt;export&lt;/code&gt; when they don't actually need it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="6c1a"&gt;&lt;strong&gt;Auditing for suspicious variables&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="2632"&gt;On any machine you’re responsible for especially one you’ve inherited or that’s been flagged for investigation:&lt;/p&gt;
&lt;pre&gt;&lt;span id="ef31"&gt;&lt;span&gt;# Check startup files for variables that shouldn't be there&lt;/span&gt;&lt;br&gt;grep -r &lt;span&gt;"LD_PRELOAD|HISTSIZE=0|SUDO_ASKPASS"&lt;/span&gt; &amp;lt;br&amp;gt;  /etc/profile.d/ ~/.bashrc ~/.bash_profile ~/.profile&lt;/span&gt;&lt;/pre&gt;
&lt;p id="caee"&gt;If any of those turn up unexpectedly, don’t assume it’s a misconfiguration. Investigate. Their presence in startup files especially &lt;code&gt;HISTSIZE=0&lt;/code&gt; and &lt;code&gt;LD_PRELOAD&lt;/code&gt; pointing to &lt;code&gt;/tmp&lt;/code&gt; is a meaningful signal, not noise.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="4e8d"&gt;That’s the full management toolkit. View, set, scope, lock, audit. Five operations that cover everything you’ll need in day-to-day work and in the more interesting situations this knowledge puts you in reach of.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="0619"&gt;Security rules most hardening guides forget&lt;/h2&gt;
&lt;p id="0042"&gt;Configuration guides cover firewalls. They cover SSH keys. They cover &lt;code&gt;fail2ban&lt;/code&gt; and port knocking and a dozen other things that are genuinely important. Environment variables don't make the list often, which is exactly why this attack surface stays underappreciated.&lt;/p&gt;
&lt;p id="7091"&gt;&lt;strong&gt;A few rules that actually matter:&lt;/strong&gt;&lt;/p&gt;
&lt;p id="7333"&gt;&lt;strong&gt;Never store secrets in plain environment variables.&lt;/strong&gt; They show up in &lt;code&gt;printenv&lt;/code&gt;. They show up in &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/environ&lt;/code&gt; readable by any process running as the same user. They show up in crash dumps, in CI logs when a build fails mid-run, and in container inspection output if your orchestration config is even slightly misconfigured. Use a secrets manager. Pass secrets through files with tight permissions, not shell exports.&lt;/p&gt;
&lt;p id="a101"&gt;&lt;strong&gt;Lock down critical variables in scripts:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="63b4"&gt;&lt;span&gt;readonly&lt;/span&gt; PATH=&lt;span&gt;"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"&lt;/span&gt;&lt;br&gt;&lt;span&gt;readonly&lt;/span&gt; SECURE_VAR=&lt;span&gt;"value"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="0bb5"&gt;&lt;strong&gt;Harden your history file:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="02d5"&gt;&lt;span&gt;chmod&lt;/span&gt; 600 ~/.bash_history&lt;/span&gt;&lt;/pre&gt;
&lt;p id="0fc3"&gt;The default permissions on bash_history are often more permissive than they should be. Other users on a shared system can read your command history. One command does the job.&lt;/p&gt;
&lt;p id="faff"&gt;&lt;strong&gt;Audit environment files on every box you manage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="b995"&gt;grep -r &lt;span&gt;"LD_PRELOAD|HISTSIZE=0|SUDO_ASKPASS"&lt;/span&gt; &amp;lt;br&amp;gt;  /etc/profile.d/ ~/.bashrc ~/.profile ~/.bash_profile&lt;/span&gt;&lt;/pre&gt;
&lt;p id="15f9"&gt;Run this on any system you’ve inherited, any container base image you didn’t build yourself, any machine that’s been flagged in an incident. Unexpected results here aren’t curiosities they’re findings.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="4a6b"&gt;Conclusion&lt;/h2&gt;
&lt;p id="4444"&gt;Here’s a slightly uncomfortable opinion: most Linux security hardening guides are written for people who already know this stuff. They assume you understand the environment variable layer, so they skip it entirely and go straight to network rules and access controls. That assumption leaves a real gap one that shows up repeatedly in CTF writeups, in post-incident reports, and in the gap between developers who use Linux and engineers who understand it.&lt;/p&gt;
&lt;p id="fecf"&gt;Environment variables are not a trivia topic. They’re the control plane for every process running on your system. The 30+ variables in this article aren’t exhaustive they’re the ones that matter most, the ones that explain the most behavior, and the ones that show up when things go wrong or when someone with bad intentions goes right.&lt;/p&gt;
&lt;p id="434f"&gt;The terminal is the most honest interface on any system. It hides nothing if you know what to ask. Start asking better questions.&lt;/p&gt;
&lt;p id="c438"&gt;As containers and srverless architectures continue to take over infrastructure, environment variables are increasingly &lt;em&gt;the&lt;/em&gt; primary configuration layer injected at runtime, scoped per service, and almost never audited properly. That makes this knowledge more relevant every year, not less.&lt;/p&gt;
&lt;p id="dbe9"&gt;Go through this list on a test machine. Run each export. Watch what changes. The best way to internalize this is to break something deliberately in a safe environment and understand exactly why it broke.&lt;/p&gt;
&lt;p id="e518"&gt;And if you’ve got a cursed environment variable story a HISTSIZE=0 you found where it shouldn’t be, an LD_PRELOAD incident, a production outage that traced back to TZ being unset drop it in the comments. I genuinely want to hear it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="bc95"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="ffec"&gt;

&lt;a href="https://man7.org/linux/man-pages/man8/ld.so.8.html" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Linux man page: ld.so&lt;/strong&gt;&lt;/a&gt; full documentation on LD_PRELOAD, LD_LIBRARY_PATH, LD_DEBUG behavior&lt;/li&gt;

&lt;li id="65ad"&gt;

&lt;a href="https://gtfobins.github.io" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;GTFOBins&lt;/strong&gt;&lt;/a&gt; practical reference for how environment variables feature in privilege escalation paths&lt;/li&gt;

&lt;li id="83c1"&gt;

&lt;a href="https://wiki.archlinux.org/title/Environment_variables" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Arch Wiki: Environment Variables&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;one of the clearest practical guides on scoping and management&lt;/li&gt;

&lt;li id="dfdd"&gt;

&lt;a href="https://specifications.freedesktop.org/basedir-spec/latest/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;XDG Base Directory Specification&lt;/strong&gt;&lt;/a&gt; official spec for XDG_CONFIG_HOME and related variables&lt;/li&gt;

&lt;li id="750f"&gt;

&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.html" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;OWASP Secrets Management Cheat Sheet&lt;/strong&gt;&lt;/a&gt; why plain environment variables are the wrong place for credentials&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>linux</category>
    </item>
    <item>
      <title>Software development is having a second chance. Nobody saw this coming.</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Thu, 07 May 2026 14:01:55 +0000</pubDate>
      <link>https://forem.com/dev_tips/software-development-is-having-a-second-chance-nobody-saw-this-coming-2eih</link>
      <guid>https://forem.com/dev_tips/software-development-is-having-a-second-chance-nobody-saw-this-coming-2eih</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="e3e5"&gt;&lt;/h1&gt;
&lt;h2 id="587e"&gt;&lt;em&gt;Everyone spent two years arguing about whether AI would kill the craft. Turns out it might be the thing that saves it.&lt;/em&gt;&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p id="2666"&gt;Here’s a thing nobody wants to admit out loud: while half the dev community was busy writing “AI is going to take our jobs” threads, the other half was quietly shipping more software than ever before. Solo. Faster. With fewer standups.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="45f1"&gt;There’s something deeply ironic about that.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="7f9b"&gt;For the past two years, the loudest conversation in tech has been about what AI is &lt;em&gt;taking away&lt;/em&gt; from software development. The junior roles. The craft. The thinking. And look some of that is real. Nobody’s going to pretend the job market looks the same as it did in 2022. But here’s the angle that keeps getting skipped in all those LinkedIn hot takes: software development as a &lt;em&gt;practice&lt;/em&gt; the act of building systems, shipping products, solving real problems with code is quietly having a moment.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="3158"&gt;Not a crisis. A comeback.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="70eb"&gt;GitHub recently had to redesign its entire infrastructure to handle &lt;strong&gt;30x its previous scale&lt;/strong&gt; not because of more developers, but because agentic workflows and AI-assisted development exploded so fast the old architecture couldn’t keep up. That’s not a dying field. That’s a field that just found a second gear.&lt;/p&gt;
&lt;p id="2032"&gt;This article is about that second gear.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="ea1b"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AI didn’t kill software development. It stress-tested it, stripped out the tedious parts, and handed the keys back to engineers who know what to do with them. Here’s what that actually looks like.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="128d"&gt;&lt;strong&gt;The myth of the dying craft&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="3743"&gt;Let’s get something straight. “AI is killing software development” is doing a lot of heavy lifting as a take, and most of the people repeating it are confusing two very different things: the &lt;em&gt;job market&lt;/em&gt; and the &lt;em&gt;craft&lt;/em&gt;.&lt;/p&gt;
&lt;p id="e8b0"&gt;The job market? Yeah, it’s shifting. That’s real and worth a separate conversation. But the craft the actual act of designing systems, writing code, shipping things that work is not dying. It’s redistributing. And there’s a massive difference.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="51ff"&gt;&lt;strong&gt;Here’s the tell:&lt;/strong&gt; if software development were actually collapsing, you wouldn’t see GitHub scrambling to redesign its infrastructure for 30x capacity because agentic development workflows accelerated faster than anyone predicted. You wouldn’t see repository creation, pull request activity, and API usage all trending sharply upward at the same time. Those aren’t the numbers of a dying discipline. Those are the numbers of a discipline that just removed a ceiling.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="7ca7"&gt;Think about what the day-to-day used to look like. I remember spending a full afternoon building a pagination component. Not because it was intellectually interesting. Not because it required deep thought. Because there was no faster way. Someone had to write it, so someone did. That was the job a mix of genuinely hard problems and an enormous amount of mechanical, repetitive work that just had to get done.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="9282"&gt;AI ate the second category. Almost entirely.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="c144"&gt;And this is the part people keep misreading as loss. Calculators didn’t kill mathematicians. They killed &lt;em&gt;arithmetic drudgery&lt;/em&gt;, which freed mathematicians to do more actual mathematics. Same play. Different stack. The pagination component was never the craft it was the toll booth before the craft.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2Afy10GiLvGg-agYq5C_rKKg.png"&gt;&lt;p id="be1d"&gt;The craft is still there. It just finally got a fast lane.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="1a14"&gt;&lt;strong&gt;The 50-year debt AI is finally paying off&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="60f9"&gt;Software development has been dragging a 50-year backpack of accumulated bad decisions. Bad abstractions that got copy-pasted into frameworks. Over-engineered patterns that became industry standards before anyone could stop them. Wheels reinvented so many times they stopped being round.&lt;/p&gt;
&lt;p id="dad1"&gt;Every senior dev has a version of this story. Mine involves a codebase where three different teams had independently written three different date formatting utilities none of which talked to each other, all of which were “the right way” according to whoever wrote them. Nobody meant for it to happen. It just did. Because software moves fast, documentation is always someone else’s problem, and the cost of fixing old decisions is always higher than the cost of living with them.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="f35f"&gt;For decades, that debt just… compounded. Quietly. In the walls.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="3b1a"&gt;Here’s what’s actually interesting about AI: it’s the first thing that moves fast enough to surface &lt;em&gt;and&lt;/em&gt; bypass that debt at the same time. Auth systems that used to take a week to spec out and another two to implement correctly? Afternoon work now. CI/CD pipelines that felt like a rite of passage the kind where you hadn’t really earned your DevOps stripes until you’d genuinely cried over a YAML indent? Scaffolded in minutes, debugged in context.&lt;/p&gt;
&lt;p id="152c"&gt;It’s not just that things are faster. It’s that old patterns are getting stress-tested at a pace humans alone never could have managed. The bad abstractions are getting caught earlier. The reinvented wheels are getting spotted before they ship.&lt;/p&gt;
&lt;p id="447e"&gt;Think of it like inheriting a house with thirty years of questionable DIY plumbing except now you have a contractor who can see every pipe in the wall before touching a single one.&lt;/p&gt;
&lt;p id="b32d"&gt;The debt isn’t gone. But for the first time, we have a tool that doesn’t just add to it. Sometimes it actually pays some of it back.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A1JkXV4dA2W0XnJ9TxMij_A.png"&gt;&lt;p id="c54b"&gt;That’s not a small thing. That’s kind of a big deal.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="9e00"&gt;&lt;strong&gt;One dev, infinite surface area&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="7db9"&gt;Here’s the shift that doesn’t get talked about enough and it’s the one that actually changes everything.&lt;/p&gt;
&lt;p id="31f6"&gt;The real unlock from AI-assisted development isn’t that code gets written faster. It’s that one engineer can now own and reason about dramatically more surface area than before. Not a little more. A lot more. Backend, DevOps, frontend, QA, monitoring the full stack of concerns that used to require four different people with four different specializations can now live inside one person’s working context.&lt;/p&gt;
&lt;p id="6bd3"&gt;I watched a friend ship a SaaS product in six weeks. Solo. Full auth layer, billing integration, CI/CD pipeline, a halfway decent UI. In 2020, that same scope took his startup four months with a team of three. Same person. Same skills. Wildly different output. The only meaningful variable was the tooling.&lt;/p&gt;
&lt;p id="a49c"&gt;Tools like &lt;a href="https://www.cursor.com/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;&lt;em&gt;Cursor&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;&lt;em&gt;, &lt;/em&gt;&lt;/strong&gt;&lt;a href="https://aider.chat/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;&lt;em&gt;Aider&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt;&lt;em&gt;,&lt;/em&gt; &lt;/strong&gt;and &lt;strong&gt;&lt;em&gt;Claude Code&lt;/em&gt;&lt;/strong&gt; aren’t just autocomplete on steroids. They’re surface-area expanders. They let a single engineer hold more of the system in their head or at least in their context window without dropping pieces of it on the floor.&lt;/p&gt;
&lt;p id="b96e"&gt;This is the second chance for an archetype that never quite fit the enterprise era: the solo builder. The person who could see the whole product but never had the hours to execute all of it alone. That person now has a legitimate shot.&lt;/p&gt;
&lt;p id="87b2"&gt;It’s not that one player got dramatically better overnight. It’s that the map got smaller and the spawn points moved. A solo dev in 2025 starts the game with resources that a small team in 2019 would’ve spent months unlocking.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AqI3Z_wY0Oxxdy4t5c8gIuA.png"&gt;&lt;p id="e86b"&gt;The surface area didn’t shrink. The headcount required to cover it did.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="1d98"&gt;&lt;strong&gt;What “good engineering” means now&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="4f33"&gt;Something quiet is happening to the definition of a good engineer. It’s not being announced anywhere. There’s no RFC, no industry memo, no Stack Overflow post with 4,000 upvotes explaining it. It’s just… shifting. And if you’re not paying attention, you’ll miss the transition entirely.&lt;/p&gt;
&lt;p id="de87"&gt;For most of software’s history, “good engineer” meant someone who could write correct, efficient code across a broad range of problems. The person who knew the right data structure without Googling it. Who could debug a memory leak at midnight without losing their mind. Who had enough language fluency to move fast without making a mess.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="bac3"&gt;That still matters. But it’s no longer the whole game.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="840d"&gt;A colleague of mine spent three hours chasing a bug that an AI tool introduced into a service. Genuinely frustrating, genuinely subtle. Then he spent thirty minutes giving the same tool the right context the actual constraints, the edge cases, the downstream dependencies and watched it fix the bug cleanly. The skill wasn’t in writing the fix. It was in knowing exactly what information to hand over and when.&lt;/p&gt;
&lt;p id="b31a"&gt;That’s the shift. Good engineering now has a new layer on top: judgment, taste, and the ability to reason about systems at a level where you’re directing work rather than just executing it.&lt;/p&gt;
&lt;p id="1a44"&gt;Think less “master chef who cooks every dish” and more “executive chef who knows exactly what to order, from where, and what to do when it arrives wrong.”&lt;/p&gt;
&lt;p id="a405"&gt;AI is a very fast, occasionally overconfident intern. It needs direction. It needs someone who can spot when it’s confidently wrong which, if you’ve used any of these tools seriously, you know happens more than the demos suggest.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AWOqviPEGCgXwY_IRNQRcsA.png"&gt;&lt;p id="f86d"&gt;The engineers who’ll thrive aren’t the ones who out-type AI. They’re the ones who out-think it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="b91f"&gt;&lt;strong&gt;The uncomfortable truth about the second chance&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="8583"&gt;Second chances come with conditions. This one’s no different, and it would be dishonest to skip past that part.&lt;/p&gt;
&lt;p id="a2d6"&gt;Junior developers have a harder path right now. The entry-level roles that used to absorb new grads the ones that were repetitive enough to be learnable and scoped enough to be survivable a lot of those look different now. Some are gone. That’s worth saying clearly, without dressing it up as “the market is evolving” or some other phrase that means the same thing while feeling less uncomfortable.&lt;/p&gt;
&lt;p id="3a16"&gt;But here’s where it’s worth zooming out.&lt;/p&gt;
&lt;p id="6bbd"&gt;Every major shift in this industry looked like the end from inside it. Cloud computing arrived and everyone asked why anyone would ever manage their own servers again. Docker showed up and the “but it works on my machine” era started dying. Each time, the engineers who leaned in early who treated the new thing as infrastructure to understand rather than a threat to outlast came out with more leverage, not less.&lt;/p&gt;
&lt;p id="d858"&gt;This moment is opt-in. That’s the uncomfortable part. The second chance doesn’t land in your inbox automatically. It goes to the people who decide to pick it up.&lt;/p&gt;
&lt;p id="f024"&gt;The craft is still here. The problems are still real. The systems still need someone to design them, reason about them, and take responsibility when they fall over at the worst possible moment.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="dc92"&gt;AI doesn’t do that last part. Not yet. Probably not for a while.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="1849"&gt;That gap is the opportunity.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="23e3"&gt;&lt;strong&gt;The craft didn’t die. It leveled up.&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="0f87"&gt;Here’s where I land on this, for whatever it’s worth from someone who’s been watching this industry long enough to have strong opinions about tab versus space debates that nobody asked for.&lt;/p&gt;
&lt;p id="3e37"&gt;Software development isn’t being replaced. It’s getting a difficulty reset. The grind that used to live in the boilerplate, the scaffolding, the “I’ve written this exact thing four times across three jobs” work that part is going away. And honestly, good riddance. Nobody got into this field because they loved writing the fifteenth variation of a user authentication flow.&lt;/p&gt;
&lt;p id="4dd0"&gt;The second chance is real. But it belongs to engineers who are willing to see AI as infrastructure the same way we eventually stopped arguing about whether to use the cloud and just started building on it.&lt;/p&gt;
&lt;p id="f4ef"&gt;The next decade of software will be built by fewer people, moving faster, covering more ground. The quality ceiling on everything they ship will depend almost entirely on the judgment, taste, and systems thinking of the humans steering it. That’s not a demotion. That’s actually a more interesting job than the one that came before it.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="4401"&gt;So the question worth sitting with isn’t “is AI taking over software development?”&lt;/p&gt;

&lt;p id="8cc3"&gt;It’s: are you picking up the controller or not?&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="4332"&gt;Because the game didn’t end. The respawn screen just looked a lot like a warning, and most people stopped reading before the countdown finished.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="8b73"&gt;Drop your take in the comments second chance or last chance? I want to know where you land.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="7e7c"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="2304"&gt;&lt;a href="https://github.blog/news-insights/company-news/an-update-on-github-availability/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;GitHub Blog:&lt;/strong&gt; Why GitHub had to redesign for 30x scale&lt;/a&gt;&lt;/li&gt;

&lt;li id="4688"&gt;&lt;a href="https://www.cursor.com/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Cursor&lt;/strong&gt; AI-first code editor&lt;/a&gt;&lt;/li&gt;

&lt;li id="b997"&gt;&lt;a href="https://aider.chat/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Aider&lt;/strong&gt; AI pair programming in your terminal&lt;/a&gt;&lt;/li&gt;

&lt;li id="16a3"&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code/overview" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Claude Code &lt;/strong&gt;agentic coding from the terminal&lt;/a&gt;&lt;/li&gt;

&lt;li id="72cb"&gt;&lt;a href="https://dora.dev/" rel="noopener ugc nofollow noreferrer"&gt;DORA 2025 State of DevOps Report&lt;/a&gt;&lt;/li&gt;

&lt;li id="6187"&gt;&lt;a href="https://github.blog/news-insights/octoverse/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Octoverse 2025&lt;/strong&gt; GitHub’s annual developer report&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>Kubernetes 1.36 killed your webhooks. Here are 10 other things it quietly changed.</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Thu, 07 May 2026 13:56:43 +0000</pubDate>
      <link>https://forem.com/dev_tips/kubernetes-136-killed-your-webhooks-here-are-10-other-things-it-quietly-changed-171</link>
      <guid>https://forem.com/dev_tips/kubernetes-136-killed-your-webhooks-here-are-10-other-things-it-quietly-changed-171</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="c303"&gt;Haru dropped with a Hokusai painting and a calligraphy inscription. Buried underneath all that poetry is a release that rearranged how your cluster actually works.&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;p id="d443"&gt;There’s a tradition in Kubernetes releases where the changelog reads like a corporate memo dry, dense, and written for people who already know what they’re looking for. Kubernetes 1.36 broke that tradition in the most unexpected way. The release is named &lt;em&gt;Haru&lt;/em&gt; a Japanese word that carries three meanings at once: spring, clear skies, far-off horizons. The logo is a reimagining of Hokusai’s &lt;em&gt;Red Fuji&lt;/em&gt;, with the Kubernetes helm floating in the sky above the mountain. The calligraphy brushed across it translates to&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="daa1"&gt;&lt;em&gt;“soar into clear skies; toward tomorrow’s sunrise.”&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="95c8"&gt;And I’m sitting here like okay, K8s. We’re doing this now.&lt;/p&gt;
&lt;p id="f705"&gt;The poetry is earned though, because underneath it, 1.36 is one of the more consequential releases in recent memory. Not because it introduced a dozen shiny new alpha features (it did that too), but because it finally graduated things that have been in progress for years, killed things that should’ve died ages ago, and gave platform engineers actual tools to stop duct-taping their clusters together.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="7025"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Mutating Admission Policies are GA and webhooks are on notice. User Namespaces finally hit stable after four years in alpha. Ingress NGINX is officially retired not deprecated, &lt;em&gt;retired&lt;/em&gt;. DRA grew up for real GPU scheduling. OCI volumes made ML model distribution less embarrassing. HPA scale-to-zero is now a thing. And the gitRepo volume type is gone, eight years after everyone was told it was going away.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="ea0a"&gt;&lt;strong&gt;Webhooks: cooked&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="5081"&gt;If you’ve run admission webhooks in production, you already know the drill. Every API request hits your webhook server on the way in. That server lives outside the cluster, needs its own deployment, its own TLS, its own on-call rotation. And when it goes down and it will go down pod scheduling stops. Not degrades. Stops.&lt;/p&gt;
&lt;p id="b269"&gt;I once spent a week chasing a stalled CI/CD pipeline. Deployments failing with no clear pattern, logs noisy but useless. Root cause: an OPA Gatekeeper webhook silently dropping pod CREATE requests under load. A week. For a dropped request.&lt;/p&gt;
&lt;p id="f378"&gt;That entire class of problem disappears with &lt;a href="https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/3962-mutating-admission-policies/README.md" rel="noopener ugc nofollow noreferrer"&gt;MutatingAdmissionPolicies&lt;/a&gt;, which hit GA in 1.36. Instead of an external service, you write mutation logic as CEL expressions evaluated inline inside the API server. No external server. No TLS certs to rotate. No 3am pages because the webhook pod got evicted. Define it as a Kubernetes object, version-control it in Git, ship it through your normal GitOps flow.&lt;/p&gt;
&lt;p id="2655"&gt;&lt;strong&gt;The asterisk:&lt;/strong&gt; if your mutation logic needs to call an external service, you still need a webhook. But that’s maybe 20% of real-world use cases. Label injection, sidecar prepending, field defaulting all of that is now native and in-process.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AQ6Jrcgbg4w6pXzGfz_hGuQ.jpeg"&gt;&lt;p id="53a8"&gt;Webhooks aren’t gone. They’re just no longer the only option. And for most teams, that’s a massive operational relief.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="ac6a"&gt;&lt;strong&gt;Root inside a container was always fiction&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="a57d"&gt;Here’s something nobody likes to say out loud: when your container runs as root, it’s running as root on the host too. The container boundary exists, sure but if something escapes it, it lands on your node with full administrative power. That’s not a hypothetical. Container breakout vulnerabilities are real, documented, and have CVEs attached to them.&lt;/p&gt;
&lt;p id="640f"&gt;The fix has existed in Linux for years. User Namespaces let you map UID 0 inside the container to an unprivileged user on the host. Your process thinks it’s root. The kernel knows it isn’t. If it escapes, it lands with essentially nothing.&lt;/p&gt;
&lt;p id="9d59"&gt;Kubernetes has been working toward this since v1.25 in August 2022. Four years of alpha, beta, validation on production workloads, and edge case hunting. In 1.36 it’s finally &lt;a href="https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/127-user-namespaces" rel="noopener ugc nofollow noreferrer"&gt;Stable&lt;/a&gt;. You enable it with one field:&lt;/p&gt;
&lt;pre&gt;&lt;span id="bf89"&gt;&lt;span&gt;spec:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;hostUsers:&lt;/span&gt; &lt;span&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="73b6"&gt;That’s it. That’s the fence with a lock.&lt;/p&gt;
&lt;p id="1f95"&gt;Before this, running genuinely rootless containers in Kubernetes meant layering on gVisor, Kata Containers, or some combination of third-party tooling and crossed fingers. Now it’s native, stable, and production-ready with no extra dependencies.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="1433" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AhlKRN7L6FCE0T2mabdzVtg.jpeg"&gt;&lt;p id="e20c"&gt;&lt;strong&gt;One watch-out:&lt;/strong&gt; Images built assuming real root privileges may behave unexpectedly under UID remapping. Test on non-critical workloads first before rolling it cluster-wide.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="8547"&gt;&lt;strong&gt;Ingress NGINX is dead. Not deprecated. Dead.&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="8953"&gt;On March 24, 2026, Kubernetes SIG Network and the Security Response Committee officially retired Ingress NGINX. No more releases. No bug fixes. No security patches. If you’re running it today, you’re running unsupported software in production and the maintainers have left the building.&lt;/p&gt;
&lt;p id="b946"&gt;This isn’t a soft deprecation with a two-release runway. There’s no “we recommend migrating by v1.38.” It’s done. The flaws were too deep, the maintainer bandwidth wasn’t there, and the Security Response Committee signed off on pulling the plug.&lt;/p&gt;
&lt;p id="231f"&gt;The uncomfortable part is how many production clusters are still running it. Ingress NGINX became the default answer to “how do I route traffic in Kubernetes” for years. It was good enough, it was everywhere, and nobody had a strong reason to migrate until now.&lt;/p&gt;
&lt;p id="d5ac"&gt;The migration path is &lt;a href="https://gateway-api.sigs.k8s.io/" rel="noopener ugc nofollow noreferrer"&gt;Gateway API v1.5&lt;/a&gt;. It gives you structured routing, cross-namespace references, and a proper separation between infrastructure concerns and developer concerns things Ingress never cleanly handled. The &lt;a href="https://github.com/kubernetes-sigs/ingress2gateway" rel="noopener ugc nofollow noreferrer"&gt;Ingress2Gateway project&lt;/a&gt; hit 1.0 in March 2026 specifically to help with this transition. The tooling exists. The excuses don’t.&lt;/p&gt;
&lt;p id="e6d2"&gt;If you’re on Ingress NGINX, this is the conversation to have with your team this sprint, not next quarter.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="3019"&gt;&lt;strong&gt;DRA grew a brain. GPU scheduling makes sense now.&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="49dd"&gt;If you’ve ever tried to schedule GPU workloads on Kubernetes before Dynamic Resource Allocation existed, you know what it felt like. Node selectors, custom labels, resource limits that didn’t reflect actual hardware topology, vendor-specific device plugins that all invented their own interfaces. It worked, technically. The way duct tape works, technically.&lt;/p&gt;
&lt;p id="e22d"&gt;DRA has been Kubernetes’ answer to this for a few releases now a proper framework for scheduling specialized hardware like GPUs, accelerators, and custom silicon. 1.36 is the release where it stops feeling experimental.&lt;/p&gt;
&lt;p id="aa77"&gt;A few things landed here that matter. Taints and tolerations for hardware devices: you can now take a specific GPU offline for maintenance without touching the rest of the cluster. Same mental model you already use for nodes, applied to individual devices. Resource Health Status is now surfaced through standard Kubernetes tooling if a GPU is unhealthy, it shows up like any unhealthy pod or node. No custom monitoring stack per vendor. No guessing. Just a status field that’s either green or it isn’t.&lt;/p&gt;
&lt;p id="183e"&gt;Per-pod DRA resource visibility is also locked to GA, meaning monitoring tools, billing systems, and operators can reliably query exactly what hardware each pod has been allocated. That matters a lot when your GPU cluster costs more per hour than most engineers’ daily rate.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2Ah-nrt_DgQTGEQA3TSoUXcA.jpeg"&gt;&lt;p id="51e3"&gt;The AI/ML infra angle here is obvious. Training jobs are expensive. Scheduling a 12-hour run onto a degraded GPU because your health checks lived in a vendor sidecar that missed a signal is the kind of thing that ends sprints. 1.36 starts fixing the foundation.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="1930"&gt;&lt;strong&gt;Shipping ML models in init containers was embarrassing&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="adf1"&gt;Getting a large model into a container has always been an exercise in picking the least bad option. Bake it into the image and watch your pull times become a running joke. Use an init container to download it at runtime and accept the complexity and failure modes that come with that. Fight ConfigMap size limits trying to get config artifacts in. Build a custom distribution pipeline and maintain that forever.&lt;/p&gt;
&lt;p id="39ea"&gt;None of these are good. All of them are common.&lt;/p&gt;
&lt;p id="285b"&gt;&lt;a href="https://www.perfectscale.io/blog/kubernetes-v1-36-sneak-peek" rel="noopener ugc nofollow noreferrer"&gt;OCI VolumeSource&lt;/a&gt; hits GA in 1.36 and it’s the answer that should’ve existed earlier. Reference any OCI image as a volume. Kubernetes pulls it and mounts the contents into your pod exactly like a regular volume. Your 40GB model lives in its own OCI artifact, versioned and distributed through the same registry infrastructure you already use. Your app container stays lean. Updates to the model don’t require rebuilding the app image.&lt;/p&gt;
&lt;pre&gt;&lt;span id="52bb"&gt;&lt;span&gt;volumes:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;-&lt;/span&gt; &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;model-weights&lt;/span&gt;&lt;br&gt;    &lt;span&gt;image:&lt;/span&gt;&lt;br&gt;      &lt;span&gt;reference:&lt;/span&gt; &lt;span&gt;registry.example.com/models/llama-weights:v3&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AT2WL5eenMjZczj29x4O1qA.jpeg"&gt;&lt;p id="ab13"&gt;For AI/ML workloads specifically this is a meaningful quality-of-life change. Model and code have different update cadences, different owners, and different size profiles. Treating them as separate artifacts that get composed at runtime is just the correct architecture. OCI VolumeSource makes that native instead of something you have to engineer around Kubernetes to achieve.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="5902"&gt;&lt;strong&gt;HPA scale-to-zero: serverless K8s without the serverless tax&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="0f29"&gt;Every team has them. Pods sitting at 0.3% CPU at midnight, burning compute budget, waiting for a queue message that won’t arrive until morning. You know you should scale them down. You also know that wiring up a custom scaler, a KEDA setup, or a managed FaaS layer just to handle idle workloads is its own operational surface area to maintain.&lt;/p&gt;
&lt;p id="ba7f"&gt;1.36 introduces alpha support for HPA scale-to-zero for Object and External metrics. &lt;code&gt;minReplicas: 0&lt;/code&gt; is now a real configuration, not a validation error.&lt;/p&gt;
&lt;pre&gt;&lt;span id="7fe9"&gt;&lt;span&gt;spec:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;minReplicas:&lt;/span&gt; &lt;span&gt;0&lt;/span&gt;&lt;br&gt;  &lt;span&gt;maxReplicas:&lt;/span&gt; &lt;span&gt;50&lt;/span&gt;&lt;br&gt;  &lt;span&gt;metrics:&lt;/span&gt;&lt;br&gt;    &lt;span&gt;-&lt;/span&gt; &lt;span&gt;type:&lt;/span&gt; &lt;span&gt;External&lt;/span&gt;&lt;br&gt;      &lt;span&gt;external:&lt;/span&gt;&lt;br&gt;        &lt;span&gt;metric:&lt;/span&gt;&lt;br&gt;          &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;sqs_queue_length&lt;/span&gt;&lt;br&gt;        &lt;span&gt;target:&lt;/span&gt;&lt;br&gt;          &lt;span&gt;type:&lt;/span&gt; &lt;span&gt;AverageValue&lt;/span&gt;&lt;br&gt;          &lt;span&gt;value:&lt;/span&gt; &lt;span&gt;10&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="2a16"&gt;Queue hits zero, pods go to zero. Message arrives, HPA spins them back up. Combine with Karpenter and the node drains too. That’s native scale-to-zero serverless architecture without handing control to a managed FaaS platform or bolting on a separate autoscaling tool.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AFX8jnbwBuoDgIuJqf_VoeA.jpeg"&gt;&lt;p id="7843"&gt;It’s alpha, so you need to enable the &lt;code&gt;HPAScaleToZero&lt;/code&gt; feature gate explicitly. More importantly audit your existing HPAs. If you're not explicitly setting &lt;code&gt;minReplicas: 1&lt;/code&gt; somewhere, your workloads may now behave differently than you expect. That's the kind of silent change that shows up in a production incident, not a code review.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="2e70"&gt;&lt;strong&gt;SSH-ing into nodes to check logs was always shameful&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="9500"&gt;You know the sequence. Something’s wrong on a node. You find the bastion host. You SSH in. You navigate to the worker node. You run &lt;code&gt;journalctl -u kubelet&lt;/code&gt; and scroll through walls of output trying to find the one line that explains why your pods aren't scheduling. Meanwhile the incident is live and your team is waiting.&lt;/p&gt;
&lt;p id="a00c"&gt;Every engineer who’s run Kubernetes in production has done this at least once. Most have done it more times than they’d like to admit.&lt;/p&gt;
&lt;p id="d6b6"&gt;Node log queries hit GA in 1.36. With &lt;code&gt;NodeLogQuery&lt;/code&gt; enabled on the kubelet and &lt;code&gt;enableSystemLogQuery&lt;/code&gt; set in your kubelet config, you can query node-level logs kubelet logs, system service logs directly through kubectl. No SSH. No bastion. No explaining to your security team why you needed direct node access during an incident.&lt;/p&gt;
&lt;pre&gt;&lt;span id="8f63"&gt;kubectl get --raw &lt;span&gt;"/api/v1/nodes/&amp;lt;node-name&amp;gt;/proxy/logs/kubelet"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f794"&gt;It’s not glamorous. It’s not the kind of feature that gets a conference talk. But the number of minutes lost per engineer per year to that SSH chain in production is genuinely non-trivial, and now it’s gone.&lt;/p&gt;
&lt;p id="11a7"&gt;This was SIG Windows work through &lt;a href="https://github.com/kubernetes/enhancements/issues/2258" rel="noopener ugc nofollow noreferrer"&gt;KEP-2258&lt;/a&gt;, which also means Windows nodes get full parity here something that’s historically lagged behind Linux in the observability tooling space.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="4a14"&gt;&lt;strong&gt;The kubelet was handing out too much access. Fixed.&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="4688"&gt;The kubelet exposes a gRPC API that monitoring agents, device plugins, and observability tools use to query what’s running on a node which pods are scheduled, what hardware resources they’ve been allocated, container states. Useful stuff. Stuff that a lot of tools legitimately need.&lt;/p&gt;
&lt;p id="22d4"&gt;The problem was access granularity. Getting a monitoring tool the access it needed often meant granting broad kubelet permissions. In regulated environments PCI-DSS, FedRAMP, SOC 2 that’s not a risk you can quietly accept. It’s a finding waiting to happen.&lt;/p&gt;
&lt;p id="27ce"&gt;Fine-grained kubelet API authorization hits GA in 1.36. Tools get exactly the permissions they need, scoped to the specific API surfaces they actually call. Nothing broader. The least-privilege model that should’ve been there from the start is now the stable, production-ready default.&lt;/p&gt;
&lt;p id="a89c"&gt;External ServiceAccount token signing also graduates to GA in this release. If your compliance framework requires key management outside Kubernetes’ default signing setup or you’re running in an environment where the control plane’s signing keys need to live in an external KMS this gives you a native, stable path to that without third-party workarounds.&lt;/p&gt;
&lt;p id="c425"&gt;Neither of these features will show up in a demo. Nobody’s going to tweet about kubelet auth granularity. But for teams running Kubernetes under any kind of compliance requirement, these two graduating to stable quietly removes two items from the audit findings list that have been sitting there for a while.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="9808"&gt;&lt;strong&gt;Your PVCs now tell you when they were last used&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="b888"&gt;Orphaned PersistentVolumeClaims are one of those slow, invisible cost drivers that nobody notices until someone pulls up the storage bill and starts asking uncomfortable questions. PVC gets created, workload gets deleted or redeployed, claim sits there bound and unused, and the underlying disk keeps billing. Multiply that across a busy cluster over a few months and it adds up faster than you’d think.&lt;/p&gt;
&lt;p id="40e7"&gt;Before 1.36, identifying idle PVCs meant either building a custom controller, running a third-party cleanup tool, or doing periodic manual audits none of which are things anyone actually wants to own.&lt;/p&gt;
&lt;p id="03f0"&gt;1.36 adds an &lt;code&gt;unusedSince&lt;/code&gt; timestamp field to &lt;code&gt;PersistentVolumeClaimStatus&lt;/code&gt;. The PVC protection controller now stamps it when the last pod referencing that claim is deleted or hits a terminal state. When a new pod mounts it again, the field clears back to nil.&lt;/p&gt;
&lt;pre&gt;&lt;span id="d72f"&gt;&lt;span&gt;status:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;phase:&lt;/span&gt; &lt;span&gt;Bound&lt;/span&gt;&lt;br&gt;  &lt;span&gt;unusedSince:&lt;/span&gt; &lt;span&gt;"2026-03-10T14:22:00Z"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9d0b"&gt;Two states. Either it has a timestamp idle since that moment or it’s nil, meaning it’s currently mounted or has never been used. Simple, native, queryable through standard kubectl and the API.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AZKoZD3O3wwY1r-tnVR8XUw.jpeg"&gt;&lt;p id="91bd"&gt;It’s alpha, so it’s not on by default yet. But the pattern it enables list all PVCs where &lt;code&gt;unusedSince&lt;/code&gt; is older than 30 days, review, clean up is something teams have been building custom tooling to approximate for years. Now it's just a field.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="46b6"&gt;&lt;strong&gt;The gitRepo volume: eight years after deprecation, finally gone&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="9566"&gt;Deprecated in v1.11. June 2018. That’s not a typo.&lt;/p&gt;
&lt;p id="75b2"&gt;For eight years the gitRepo volume type lived in the Kubernetes codebase like a haunted house nobody wanted to deal with. It let you populate a volume directly from a Git repository at pod startup which sounds convenient until you think about what that actually means. Arbitrary git clone operations running with pod-level permissions, no real sandboxing, a well-documented attack surface. It was deprecated almost immediately after people understood the implications.&lt;/p&gt;
&lt;p id="1a53"&gt;And yet there it sat. Release after release. Deprecation notice in place, removal perpetually deferred.&lt;/p&gt;
&lt;p id="4526"&gt;1.36 finally pulls it out. If you’re somehow still using gitRepo volumes in 2026, this is the migration you should have done in 2019. Init containers with a proper git clone step, or a CI/CD pipeline that bakes artifacts into the image, are both better in every dimension.&lt;/p&gt;
&lt;p id="a858"&gt;Also worth flagging in the same breath: &lt;code&gt;externalIPs&lt;/code&gt; in the Service spec is deprecated in 1.36, with full removal planned for v1.43. It's been a known attack vector since &lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2020-8554" rel="noopener ugc nofollow noreferrer"&gt;CVE-2020-8554&lt;/a&gt;. If it's in your configs, start the conversation now rather than doing it in a panic seven releases from now.&lt;/p&gt;
&lt;p id="8147"&gt;The broader signal here is worth noting. Kubernetes is enforcing its own deprecation timeline now, not just suggesting it. That’s good for the project’s long-term hygiene and a sign the maintainers are serious about not carrying dead weight forever.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="222d"&gt;&lt;strong&gt;Haru didn’t come to play&lt;/strong&gt;&lt;/h2&gt;
&lt;p id="b707"&gt;There’s a version of this release that gets written off as a maintenance drop. No dramatic new directions, no paradigm-shifting alpha features with a flashy demo. Just a lot of things graduating, a lot of debt getting cleared, and a handful of quiet improvements that compound over time.&lt;/p&gt;
&lt;p id="cc75"&gt;That reading misses the point entirely.&lt;/p&gt;
&lt;p id="7017"&gt;1.36 is the release where Kubernetes starts filling its own gaps instead of expecting you to build scaffolding around them. Admission webhooks were a workaround that became an institution MAPs replace the institution. Container root isolation required third-party tooling for years User Namespaces makes it native. GPU scheduling was a patchwork of vendor plugins and custom controllers DRA gives it a real foundation. ML model distribution was genuinely embarrassing OCI volumes fix the architecture.&lt;/p&gt;
&lt;p id="e1a4"&gt;None of these are new ideas. All of them are ideas that finally graduated from “you can kind of do this if you squint” to “this is stable and production-ready.”&lt;/p&gt;
&lt;p id="4906"&gt;The Ingress NGINX retirement and the gitRepo removal are the most underrated signals in the release. They tell you the project is serious about what Kubernetes should and shouldn’t be and serious about not carrying CVE-adjacent code forever because migration is inconvenient.&lt;/p&gt;
&lt;p id="4645"&gt;DRA’s trajectory over the next two or three releases will define how Kubernetes handles the AI infrastructure wave. The bones being laid in 1.36 matter more than they look.&lt;/p&gt;
&lt;p id="ce68"&gt;Haru means spring. New season, cleared skies, distant horizon. The release name was earned.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="7dfd"&gt;Which of these 10 changes hits closest to home for your cluster? Drop it in the comments.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="c854"&gt;&lt;strong&gt;Helpful resources&lt;/strong&gt;&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="05ed"&gt;&lt;a href="https://kubernetes.io/blog/2026/04/22/kubernetes-v1-36-release/" rel="noopener ugc nofollow noreferrer"&gt;Kubernetes v1.36 official release blog&lt;/a&gt;&lt;/li&gt;

&lt;li id="5ca5"&gt;&lt;a href="https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/3962-mutating-admission-policies/README.md" rel="noopener ugc nofollow noreferrer"&gt;KEP-3962: MutatingAdmissionPolicies&lt;/a&gt;&lt;/li&gt;

&lt;li id="c86d"&gt;&lt;a href="https://github.com/kubernetes/enhancements/tree/master/keps/sig-node/127-user-namespaces" rel="noopener ugc nofollow noreferrer"&gt;KEP-127: User Namespaces&lt;/a&gt;&lt;/li&gt;

&lt;li id="f02e"&gt;&lt;a href="https://gateway-api.sigs.k8s.io/" rel="noopener ugc nofollow noreferrer"&gt;Gateway API v1.5 docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="de04"&gt;&lt;a href="https://github.com/kubernetes-sigs/ingress2gateway" rel="noopener ugc nofollow noreferrer"&gt;Ingress2Gateway migration tool&lt;/a&gt;&lt;/li&gt;

&lt;li id="110c"&gt;&lt;a href="https://www.cloudraft.io/blog/kubernetes-v1-36-haru-features-upgrade-guide" rel="noopener ugc nofollow noreferrer"&gt;Cloudraft v1.36 upgrade guide&lt;/a&gt;&lt;/li&gt;

&lt;li id="35b9"&gt;&lt;a href="https://palark.com/blog/kubernetes-1-36-release-features/" rel="noopener ugc nofollow noreferrer"&gt;Palark DRA deep dive&lt;/a&gt;&lt;/li&gt;

&lt;li id="f8e5"&gt;&lt;a href="https://diginomica.com/kubernetes-v136-haru-security-gpus-and-observability-grow" rel="noopener ugc nofollow noreferrer"&gt;Diginomica interview with Release Lead Ryota Sawada&lt;/a&gt;&lt;/li&gt;

&lt;li id="5e1b"&gt;&lt;a href="https://nvd.nist.gov/vuln/detail/CVE-2020-8554" rel="noopener ugc nofollow noreferrer"&gt;CVE-2020–8554 — externalIPs attack vector&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>kubernetes</category>
      <category>devops</category>
    </item>
    <item>
      <title>The AI price hike that never showed up on the pricing page (your bill went up 27% anyway)</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Sun, 03 May 2026 02:00:09 +0000</pubDate>
      <link>https://forem.com/dev_tips/the-ai-price-hike-that-never-showed-up-on-the-pricing-page-your-bill-went-up-27-anyway-3mn5</link>
      <guid>https://forem.com/dev_tips/the-ai-price-hike-that-never-showed-up-on-the-pricing-page-your-bill-went-up-27-anyway-3mn5</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="5585"&gt;Anthropic changed one component most developers have never heard of. Your wallet felt it before your brain did.&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;p id="15f6"&gt;Getting ripped off cleanly is almost respectable. Price goes up, you see it, you rage-tweet about it, you maybe switch providers. Transactional. At least everyone’s being honest about what’s happening.&lt;/p&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p id="b5b5"&gt;The move Anthropic just pulled is the other kind. The sneaky kind. The kind where the pricing page stays completely untouched, the model name barely changes, and your bill climbs 27% while you’re busy actually shipping things. By the time you notice, you’ve already paid for three months of the new reality.&lt;/p&gt;
&lt;p id="1bd5"&gt;&lt;strong&gt;Here’s what happened:&lt;/strong&gt; Claude Opus 4.7 shipped with the same per-token price as Opus 4.6 $5 input, $25 output per million tokens. Same numbers. Same page. But hiding underneath that was a new tokenizer the component that sits between your text and the model and decides how many tokens your words are worth. The new one is more aggressive. Same sentence, more tokens. More tokens, bigger bill. No announcement that said “hey, this is effectively a price increase.” Just a changelog note and a Ramp analysis that did the math nobody wanted to do.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2ARADmmOa-gcr8CY1P9M-7Ug.jpeg"&gt;&lt;p id="3bc2"&gt;And look this isn’t a villain origin story. AI compute is brutal, these companies are hemorrhaging money, and the subsidized pricing era was always going to end. But there’s a difference between raising your prices and quietly changing the unit of measurement. One is a business decision. The other is a choice about how much you respect your users.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="ce86"&gt;So let’s talk about the actual mechanic, because once you understand it, you’ll never read an AI pricing page the same way again.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="a2ea"&gt;What is a tokenizer (and why you’ve been ignoring the wrong number)&lt;/h2&gt;
&lt;p id="dadf"&gt;Before we get to the pricing trick, you need to understand the component doing the dirty work because most developers, even ones building on top of these APIs daily, couldn’t tell you what a tokenizer actually does beyond “it counts words, right?”&lt;/p&gt;
&lt;p id="9ba9"&gt;It does not count words.&lt;/p&gt;
&lt;p id="1ee0"&gt;A tokenizer is the layer that sits between your raw text and the model itself. Its job is to break your input into tokens chunks of meaning the model can process numerically. Sometimes a token is a full word. Sometimes it’s half a word. Sometimes it’s just punctuation. The word “tokenization” itself splits into three tokens in most modern tokenizers: &lt;code&gt;token&lt;/code&gt;, &lt;code&gt;ization&lt;/code&gt;, and maybe a prefix character. "Hello" is one token. "Antidisestablishmentarianism" is five.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="852c"&gt;&lt;strong&gt;Quick mental model:&lt;/strong&gt; Think of a tokenizer like a bouncer at a club deciding how many people count as a “group.” Same party, different bouncer, different headcount at the door and you’re paying per head.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="4be1"&gt;Here’s why this matters technically: the model never sees your sentence. It sees a sequence of integers each one a row ID pointing to an entry in the model’s embedding table, which maps that token to a high-dimensional vector of numbers. Those vectors encode meaning, not as a dictionary definition but as a position in semantic space relative to every other token the model knows.&lt;/p&gt;
&lt;p id="f915"&gt;“Dog” and “cat” are closer together in that space than “dog” and “carburetor.” The model understands relationships, not definitions. And all of that understanding starts from the tokenizer’s output.&lt;/p&gt;
&lt;p id="33fd"&gt;The part that actually hits your bill is this: both of the model’s core operations attention and the feed-forward layers scale with token count. Attention is O(L²) where L is the number of tokens in your sequence. More tokens don’t just add cost linearly. They compound it. A 20% longer token sequence doesn’t cost 20% more to process it costs meaningfully more, especially at longer context lengths.&lt;/p&gt;
&lt;p id="ecd9"&gt;&lt;strong&gt;The kicker: tokenizers are not part of the model.&lt;/strong&gt; They’re external. Swappable. And completely within the lab’s control to change between model versions without touching the price card.&lt;/p&gt;
&lt;p id="e527"&gt;Which is exactly what makes them such a clean lever to pull.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="9132"&gt;&lt;strong&gt;Try it yourself:&lt;/strong&gt; Paste any paragraph into &lt;a href="https://tiktokenizer.vercel.app" rel="noopener ugc nofollow noreferrer"&gt;tiktokenizer.vercel.app&lt;/a&gt; and switch between GPT-4o and Llama 3 tokenizers. Watch the token count shift on identical text. That delta is real money at scale.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="22f9"&gt;The Opus 4.7 case: same price, different math, bigger bill&lt;/h2&gt;
&lt;p id="45c5"&gt;This is where it gets concrete. And slightly infuriating.&lt;/p&gt;
&lt;p id="9bad"&gt;When Anthropic released Claude Opus 4.7, they kept the token pricing identical to Opus 4.6 $5 per million input tokens, $25 per million output tokens. If you skimmed the announcement like most people do, you saw “new model, same price” and moved on. Reasonable. Normal. Except the thing they quietly swapped was the tokenizer.&lt;/p&gt;
&lt;p id="516d"&gt;Opus 4.7 uses a new tokenizer most likely inherited from Mythos, the underlying architecture it was distilled from. And that new tokenizer is more granular. It breaks text into smaller chunks, which means more tokens per sentence, per prompt, per API call. Independent testing put the increase at up to &lt;strong&gt;35% more tokens&lt;/strong&gt; for the same input text. Ramp ran their own analysis across real enterprise workloads and landed on a &lt;strong&gt;12–27% higher effective cost&lt;/strong&gt; depending on use case despite the per-token price being identical.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="419a"&gt;“It’s like a pizza place quietly cutting their slices thinner. Same pizza. Same price per slice. Somehow you’re buying more slices for dinner.”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="8659"&gt;Let’s put numbers to it. Say you’re running a prompt that previously tokenized to 1,000 input tokens. At $5 per million, that’s $0.005 per call. With the new tokenizer inflating that to 1,350 tokens, you’re now at $0.00675 per call. Alone that looks tiny. At 10 million calls a month which is not a large production system that’s a swing from $50,000 to $67,500. Monthly. That’s a $17,500 difference that showed up in your bill but not in any pricing announcement.&lt;/p&gt;
&lt;p id="ca17"&gt;&lt;strong&gt;Comparison table (embed this as a clean white-bg visual):&lt;/strong&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="258" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AwlFCT47cbZTr4amoQr8mSw.png"&gt;&lt;p id="b88a"&gt;What makes this particularly sharp is that Anthropic did technically mention the tokenizer change. It was in the release notes. One line. No mention of cost implications. No calculator. No migration guide for teams running cost-sensitive workloads. Just a changelog entry that assumed you knew what a tokenizer was and would do the math yourself.&lt;/p&gt;
&lt;p id="ac04"&gt;Most teams didn’t. Most teams found out the normal way when the finance team forwarded the invoice with a “can you explain this?” and you had to go spelunking through model release notes at the worst possible time.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="c1aa"&gt;&lt;strong&gt;Note:&lt;/strong&gt; This isn’t unique to Anthropic. Llama 3’s tokenizer generates ~25% more tokens than GPT-4o’s on equivalent English text. Every time you benchmark models on price, you need to benchmark the tokenizer too or you’re comparing the menu price, not the actual meal cost.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AnpMPKDGOXIpRJWvPhywJqg.jpeg"&gt;&lt;p id="4fc6"&gt;The pricing page isn’t lying to you. It’s just not telling you the whole truth. And in production, that gap is expensive.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="55e7"&gt;Why this is happening now (and it’s not going to stop)&lt;/h2&gt;
&lt;p id="5d26"&gt;Let’s be real for a second. None of this happened in a vacuum.&lt;/p&gt;
&lt;p id="13a6"&gt;The era of “AI is basically free, just use it” was always venture capital in a trench coat pretending to be a business model. OpenAI, Anthropic, Google every major lab has been running inference at a loss for years, subsidizing your $20/month subscription and your cheap API calls with billions of dollars in funding that was buying market share, not profit. The pitch was: get developers hooked, get enterprises dependent, figure out the margin problem later.&lt;/p&gt;
&lt;p id="b6e2"&gt;Later is now.&lt;/p&gt;
&lt;p id="317b"&gt;Anthropic is reportedly heading toward an IPO. OpenAI already closed a funding round that values it at levels that demand a credible path to profit. The compute costs haven’t dropped fast enough. The revenue, while genuinely impressive in growth rate, still doesn’t cover the infrastructure spend in nominal terms. And the investors who wrote the nine-figure checks are starting to ask the question every investor eventually asks: so when exactly does this make money?&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="85ac"&gt;“The subsidized pricing era wasn’t a gift. It was a customer acquisition strategy with a very long expiry date and that date just passed.”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="ba53"&gt;This puts labs in a genuinely uncomfortable position. Raising headline prices is a PR event. Every tech publication runs the comparison. Developers tweet about it. Enterprise procurement teams use it as leverage. It’s visible, trackable, and creates churn risk.&lt;/p&gt;
&lt;p id="428e"&gt;But changing a tokenizer? That’s a technical decision buried in a release note. Most customers aren’t sophisticated enough to catch it and the labs know that. It’s not malicious genius, it’s just the path of least resistance when you need revenue without a news cycle.&lt;/p&gt;
&lt;p id="c4bc"&gt;The uncomfortable truth is that the people building on these APIs professionally the ones running real workloads, tracking cost per query, building cost-sensitive products are exactly the customers who will catch this and push back. And they are. Ramp caught it. Developer forums are full of threads comparing token counts across model versions. The information exists. It’s just not surfaced by default.&lt;/p&gt;
&lt;p id="076a"&gt;I spent a sprint mid-project recently realizing our API costs had drifted 20% over six weeks with no code changes on our end. No new features. No traffic spike. Just a model version bump in a dependency that auto-updated, and a tokenizer that quietly decided our prompts were worth more tokens than before. The kind of debugging session that feels insane until you understand what you’re actually looking at.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="1a7b"&gt;&lt;strong&gt;The broader pattern:&lt;/strong&gt; This isn’t just Anthropic. As every major lab races toward profitability, expect tokenizer efficiency to become a competitive axis that cuts both ways some models will get more efficient to win cost-sensitive workloads, others will quietly inflate to juice revenue. You need to be measuring both.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2ANflhIf4PfHV4aWYeIOW7UA.jpeg"&gt;&lt;p id="b517"&gt;The labs aren’t evil. They’re just companies with real financial pressure making rational short-term decisions. But rational for them and transparent to you are two different things and right now the gap between those two is showing up as a line item on your invoice.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="b313"&gt;How to stop your token budget from bleeding out quietly&lt;/h2&gt;
&lt;p id="013c"&gt;Knowing the trick is step one. Not paying for it is step two.&lt;/p&gt;
&lt;p id="98a1"&gt;The good news is that once you understand what’s actually happening, the counter-moves are straightforward. None of this requires switching providers or rebuilding your stack. It’s mostly instrumentation you should have had anyway you just didn’t know you needed it for this specific reason.&lt;/p&gt;
&lt;h3 id="4cb1"&gt;&lt;strong&gt;1. Benchmark the tokenizer, not just the price&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="2d20"&gt;Before you migrate to any new model version even a minor bump run your actual production prompts through both tokenizers and compare counts. Not sample prompts. Not the demo text from the docs. Your real prompts, the ones your system sends at 3am on a Tuesday when nobody’s watching.&lt;/p&gt;
&lt;p id="84ec"&gt;The &lt;a href="https://tiktokenizer.vercel.app" rel="noopener ugc nofollow noreferrer"&gt;tiktokenizer playground&lt;/a&gt; lets you switch tokenizers and compare counts visually. For programmatic benchmarking, OpenAI’s &lt;code&gt;tiktoken&lt;/code&gt; library and Hugging Face's &lt;code&gt;tokenizers&lt;/code&gt; package both let you run this locally before you commit to anything.&lt;/p&gt;
&lt;pre&gt;&lt;span id="e53c"&gt;&lt;span&gt;import&lt;/span&gt; tiktoken&lt;br&gt;&lt;br&gt;old = tiktoken.encoding_for_model(&lt;span&gt;"gpt-4o"&lt;/span&gt;)&lt;br&gt;new = tiktoken.encoding_for_model(&lt;span&gt;"gpt-4"&lt;/span&gt;)&lt;br&gt;prompt = &lt;span&gt;"Your actual production prompt here"&lt;/span&gt;&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(&lt;span&gt;f"Old tokenizer: &lt;span&gt;{&lt;span&gt;len&lt;/span&gt;(old.encode(prompt))}&lt;/span&gt; tokens"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(&lt;span&gt;f"New tokenizer: &lt;span&gt;{&lt;span&gt;len&lt;/span&gt;(new.encode(prompt))}&lt;/span&gt; tokens"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(&lt;span&gt;f"Delta: &lt;span&gt;{&lt;span&gt;len&lt;/span&gt;(new.encode(prompt)) - &lt;span&gt;len&lt;/span&gt;(old.encode(prompt))}&lt;/span&gt; tokens"&lt;/span&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="4d65"&gt;Do this before every model migration. Make it a checklist item. It takes ten minutes and can save you a five-figure surprise on next month’s invoice.&lt;/p&gt;
&lt;h3 id="3104"&gt;&lt;strong&gt;2. Track cost per query, not just total spend&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="5b8d"&gt;Total API spend is a lagging indicator. By the time it looks wrong, you’ve already paid for weeks of the new reality. What you want is cost per query average token spend per API call, tracked over time.&lt;/p&gt;
&lt;p id="9b86"&gt;If that number drifts upward without a corresponding change in your prompt logic or traffic, something upstream changed. Could be a model update. Could be a dependency quietly bumping versions. Could be a tokenizer. Either way, you catch it in days instead of months.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="d04d"&gt;&lt;strong&gt;Quick setup:&lt;/strong&gt; Log &lt;code&gt;prompt_tokens&lt;/code&gt; and &lt;code&gt;completion_tokens&lt;/code&gt; from every API response. Both are returned in the usage object on every call you're already paying for that data, you might as well read it. Pipe it into whatever observability stack you're already running.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h3 id="a535"&gt;&lt;strong&gt;3. Compress your prompts deliberately&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="42af"&gt;Shorter prompts aren’t just cleaner they’re cheaper, and they’re proportionally cheaper with aggressive tokenizers. A few habits that actually move the needle:&lt;/p&gt;
&lt;ul&gt;

&lt;li id="fe21"&gt;

&lt;strong&gt;Remove filler instructions.&lt;/strong&gt; “Please make sure to carefully consider the following context before responding” is about 15 tokens of nothing. “Context:” is two.&lt;/li&gt;

&lt;li id="599d"&gt;

&lt;strong&gt;Use structured formats.&lt;/strong&gt; JSON and markdown tokenize more efficiently than verbose prose instructions in most modern tokenizers.&lt;/li&gt;

&lt;li id="6302"&gt;

&lt;strong&gt;Audit your system prompts.&lt;/strong&gt; System prompts run on every single call. A bloated system prompt that made sense when tokens were cheap hits differently now.&lt;/li&gt;

&lt;li id="f57b"&gt;

&lt;strong&gt;Cache repeated context.&lt;/strong&gt; If you’re sending the same background context on every call, look at prompt caching Anthropic and OpenAI both support it, and it’s designed exactly for this.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="5707"&gt;&lt;strong&gt;4. Pin your model versions in production&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="8182"&gt;Auto-updating to the latest model version sounds like good hygiene. In practice, it’s how you wake up to a 27% cost increase with no warning. Pin your model strings explicitly in production configs. Treat model upgrades like dependency upgrades intentional, tested, with a cost benchmark step before merge.&lt;/p&gt;
&lt;pre&gt;&lt;span id="b12f"&gt;&lt;span&gt;# Don't do this in production&lt;/span&gt;&lt;br&gt;model = &lt;span&gt;"claude-opus-latest"&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# Do this&lt;/span&gt;&lt;br&gt;model = &lt;span&gt;"claude-opus-4-6"&lt;/span&gt;  &lt;span&gt;# Pinned. Upgrade intentionally.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;h3 id="b2c7"&gt;&lt;strong&gt;5. Use the right model for the job&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="e19e"&gt;Frontier models with aggressive new tokenizers are increasingly overkill for a lot of tasks. Routing simpler queries to smaller, cheaper models and reserving the frontier model for genuinely complex reasoning is the most underused cost optimization in the industry right now.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="f787"&gt;&lt;strong&gt;Tools worth knowing:&lt;/strong&gt; &lt;a href="https://smith.langchain.com" rel="noopener ugc nofollow noreferrer"&gt;LangSmith&lt;/a&gt; for tracing token usage per chain, &lt;a href="https://helicone.ai" rel="noopener ugc nofollow noreferrer"&gt;Helicone&lt;/a&gt; for API observability across providers, &lt;a href="https://github.com/openai/openai-cookbook" rel="noopener ugc nofollow noreferrer"&gt;OpenAI Cookbook&lt;/a&gt; for prompt optimization patterns that apply across most major APIs.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="a0fe"&gt;The quiet tax on building with AI&lt;/h2&gt;
&lt;p id="a804"&gt;Here’s the thing nobody wants to say out loud: the golden era of cheap AI APIs was not a sustainable business. It was a land grab dressed up as a pricing model. And the labs that ran it Anthropic, OpenAI, everyone knew it. The developers who built on top of it mostly knew it too, in the vague way you know a party has to end eventually but you stay anyway because the drinks are still free.&lt;/p&gt;
&lt;p id="63eb"&gt;The drinks aren’t free anymore. They just haven’t updated the menu yet.&lt;/p&gt;
&lt;p id="f054"&gt;What happened with Opus 4.7 isn’t a one-time thing. It’s a preview of how pricing pressure gets absorbed when you can’t raise headline numbers without a news cycle. Tokenizer changes, context window adjustments, subtle shifts in how output is counted these are the tools available to a company that needs more revenue but can’t afford the optics of a price hike. Expect more of them. Expect them to be buried in changelogs. Expect to need actual instrumentation to catch them.&lt;/p&gt;
&lt;p id="8354"&gt;The developers who come out ahead in this environment aren’t the ones rage-tweeting about it they’re the ones who built cost observability into their stack before it became urgent, who benchmark tokenizers before migrations, who treat model upgrades with the same discipline as dependency upgrades. It’s not glamorous. It’s just the part of building on top of third-party infrastructure that nobody writes the excited blog post about.&lt;/p&gt;
&lt;p id="92f4"&gt;The open-source path is also getting more real every month. Llama, Mistral, Qwen the capability gap that justified frontier API prices is narrowing faster than the labs would like to admit. For a lot of production workloads, the math is already there. The barrier isn’t capability anymore; it’s operational overhead. That calculus shifts as tooling matures.&lt;/p&gt;
&lt;p id="9b82"&gt;For now: read the changelogs. Benchmark the tokenizer. Pin your model versions. And the next time a lab ships a new model at “the same price,” check what’s in the parentheses.&lt;/p&gt;
&lt;p id="8f30"&gt;Because apparently that’s where the 27% lives.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="90fa"&gt;Drop your take in the comments have you caught a tokenizer-driven cost drift in production? How did you find it? Would love to compare notes.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="353c"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="04a6"&gt;

&lt;a href="https://tiktokenizer.vercel.app" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Tiktokenizer playground&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;compare token counts across models visually&lt;/li&gt;

&lt;li id="b887"&gt;

&lt;a href="https://www.anthropic.com/pricing" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Anthropic pricing page&lt;/strong&gt;&lt;/a&gt; the official numbers (read alongside the changelog)&lt;/li&gt;

&lt;li id="e503"&gt;

&lt;a href="https://ramp.com" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Ramp’s Opus 4.7 cost analysis&lt;/strong&gt;&lt;/a&gt; the enterprise spend breakdown that started this conversation&lt;/li&gt;

&lt;li id="e12a"&gt;

&lt;a href="https://github.com/openai/openai-cookbook" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;OpenAI Cookbook prompt optimization&lt;/strong&gt;&lt;/a&gt; patterns that apply across most APIs&lt;/li&gt;

&lt;li id="c778"&gt;

&lt;a href="https://helicone.ai" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Helicone&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;API observability across providers&lt;/li&gt;

&lt;li id="46cc"&gt;

&lt;a href="https://smith.langchain.com" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;LangSmith&lt;/strong&gt;&lt;/a&gt; token tracing per chain&lt;/li&gt;

&lt;li id="fdc2"&gt;

&lt;a href="https://huggingface.co/docs/tokenizers" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Hugging Face tokenizers&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;run tokenizer benchmarks locally&lt;/li&gt;

&lt;li id="b63b"&gt;

&lt;a href="https://github.com/openai/tiktoken" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;OpenAI tiktoken&lt;/strong&gt;&lt;/a&gt; Python library for token counting&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>10 Best Machine Learning (ML/AI) Tools for Kubernetes Resource Optimization:</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Thu, 23 Apr 2026 16:56:41 +0000</pubDate>
      <link>https://forem.com/dev_tips/10-best-machine-learning-mlai-tools-for-kubernetes-resource-optimization-2o0i</link>
      <guid>https://forem.com/dev_tips/10-best-machine-learning-mlai-tools-for-kubernetes-resource-optimization-2o0i</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="2eb8"&gt;Kubernetes is strong yet, if improperly controlled, can be a resource hogging tool. These ten artificial intelligence-driven tools will enable you to automatically scale with intelligence, maximize performance, and reduce expenses.&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="533" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2ABy49_xkJjHbpz_HLaXMUMw.png"&gt;&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;h3 id="4379"&gt;Why AI for Kubernetes Optimization?&lt;/h3&gt;
&lt;p id="04de"&gt;Kubernetes has transformed container orchestration, simplifying deployment, scaling, and management of applications. Realistically, though, optimizing Kubernetes resources is challenging.&lt;/p&gt;
&lt;ul&gt;

&lt;li id="678f"&gt;Oversawing causes lost cloud expenditure.&lt;/li&gt;

&lt;li id="bc2c"&gt;Under-provisioning leads to problems with performance.&lt;/li&gt;

&lt;li id="79ce"&gt;Manual scalability? Forget about it; it is ineffective.&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="f497"&gt;Here artificial intelligence (AI) and machine learning (ML) find application. By analyzing workload patterns, estimating resource use, and automating scaling decisions, AI-powered products help companies save both money and headaches.&lt;/p&gt;
&lt;p id="4142"&gt;10 of the greatest AI-driven Kubernetes resource optimization tools each with a different approach to increase efficiency, lower waste, and maintain seamless workloads running are discussed in this post.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="d83f"&gt;Kubeflow: AI-Powered Kubernetes Resource Management&lt;/h2&gt;
&lt;h3 id="a0bb"&gt;what it is:&lt;/h3&gt;
&lt;p id="62b5"&gt;Kubeflow is a framework for machine learning created especially for Kubernetes. It enables DevOps teams and data scientists effectively train, implement, and scale ML models.&lt;/p&gt;
&lt;p id="8de3"&gt;&lt;strong&gt;Why is Kubernetes optimization great?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="511d"&gt;Allots resources and runs ML pipelines automatically.&lt;/li&gt;

&lt;li id="eb76"&gt;optimally uses CPU and GPU by means of AI-based scheduling.&lt;/li&gt;

&lt;li id="237e"&gt;Perfectly interacts with Kubernetes to distribute work.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="92eb"&gt;Ideal for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="7ead"&gt;AI/ML projects&lt;/li&gt;

&lt;li id="bf30"&gt;Effective GPU allocation&lt;/li&gt;

&lt;li id="0708"&gt;Large-scale ML model training&lt;/li&gt;

&lt;/ul&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="2959"&gt;KEDA: Event-Based Scaling Driven by Artificial Intelligence&lt;/h2&gt;
&lt;h3 id="c791"&gt;What is it?&lt;/h3&gt;
&lt;p id="9dae"&gt;By scaling workloads depending on real-time events instead than only CPU or memory use, KEDA (Kubernetes Event-Driven Autoscaler) expands Kubernetes’ natural autoscaling capabilities.&lt;/p&gt;
&lt;p id="2571"&gt;&lt;strong&gt;Why is it so ideal for Kubernetes optimization?&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="0ed3"&gt;responds to real-time workload surges.&lt;/li&gt;

&lt;li id="8d74"&gt;Works with RabbitMQ, AWS SQS, and Kafka among event sources.&lt;/li&gt;

&lt;li id="9bef"&gt;Cost-efficient scaling.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="f3dc"&gt;Ideal for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="8728"&gt;apps driven by events&lt;/li&gt;

&lt;li id="a011"&gt;Workloads devoid of servers&lt;/li&gt;

&lt;li id="92b7"&gt;Economical scaling&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="fe4d"&gt;🔗 &lt;a href="https://github.com/kedacore/keda" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;KEDA GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="e23f"&gt;VPA, Vertical Pod Autoscaler: AI-Powered Resource Right-Sizing&lt;/h2&gt;
&lt;h3 id="2d3d"&gt;What is it?&lt;/h3&gt;
&lt;p id="1a48"&gt;Based on past usage, VPA automatically changes CPU and memory requirements for running pods, therefore eliminating the need for hand resource adjustment.&lt;/p&gt;
&lt;h3 id="0e9a"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="c73d"&gt;lowers cloud expenses and stops over-provisioning.&lt;/li&gt;

&lt;li id="40be"&gt;Works alongside horizontal pod autoscaler Kubernetes HPA.&lt;/li&gt;

&lt;li id="e732"&gt;Changes pod resource requests constantly depending on real-time needs.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="0bb6"&gt;Excellent for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="5c3f"&gt;Avoiding underused resources&lt;/li&gt;

&lt;li id="1de6"&gt;Cost optimization for varying task load&lt;/li&gt;

&lt;li id="37ad"&gt;Right-sizing automated pod&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="2f32"&gt;🔗 &lt;a href="https://github.com/kubernetes/autoscaler/tree/master/vertical-pod-autoscaler" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Kubernetes VPA Docs&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="6cd6"&gt;Intelligent Node Scaling: OpenAI Cluster Autoscaler&lt;/h2&gt;
&lt;h3 id="c515"&gt;This is what it is.&lt;/h3&gt;
&lt;p id="ca1c"&gt;Based on real-time workload requirements, an AI-powered cluster autoscaler automatically changes Kubernetes node count.&lt;/p&gt;
&lt;h3 id="9ffa"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="72b4"&gt;guarantees just-in- time scaling, hence lowering cloud expenses.&lt;/li&gt;

&lt;li id="95d3"&gt;reduces wasted resources by closing off inactive nodes.&lt;/li&gt;

&lt;li id="c43d"&gt;works on on-prem Kubernetes clusters, AWS, GCP, Azure.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="88e3"&gt;Ideal for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="b4f9"&gt;somewhat cheap node scaling&lt;/li&gt;

&lt;li id="c819"&gt;Applications born in the clouds&lt;/li&gt;

&lt;li id="6cbb"&gt;Kubernetes configurations across many clouds&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="41bb"&gt;🔗 &lt;a href="https://github.com/kubernetes/autoscaler/tree/master/cluster-autoscaler" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Cluster Autoscaler Docs&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="c5a0"&gt;Prometheus + Thanos: Anomaly Detection Driven by AI&lt;/h2&gt;
&lt;h3 id="642f"&gt;What it is:&lt;/h3&gt;
&lt;p id="b3e2"&gt;Prometheus is a real-time monitoring tool; Thanos extends this using long-term data storage and AI-driven anomaly detection.&lt;/p&gt;
&lt;h3 id="92ec"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="f1bf"&gt;Notifies teams before failures start by spotting unusual resource use.&lt;/li&gt;

&lt;li id="119e"&gt;ML-powered analytics allows one to predict workload trends.&lt;/li&gt;

&lt;li id="8a8c"&gt;uses Grafana for elegant visuals.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="fafe"&gt;Ideally for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="7354"&gt;Kubernetes monitoring driven by artificial intelligence&lt;/li&gt;

&lt;li id="5eea"&gt;Forecasting analytics&lt;/li&gt;

&lt;li id="3ec7"&gt;Steering clear of resource congestion&lt;/li&gt;

&lt;/ul&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="8b95"&gt;Goldilocks: Discover the Ideal Source Limit&lt;/h2&gt;
&lt;h3 id="2699"&gt;What it is:&lt;/h3&gt;
&lt;p id="2962"&gt;Using Vertical Pod Autoscaler recommendations, Goldilocks aids in teams determining appropriate resource needs and restrictions.&lt;/p&gt;
&lt;h3 id="2517"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="f733"&gt;prevents both over- and under-provisioning.&lt;/li&gt;

&lt;li id="d991"&gt;guarantees that workloads precisely meet their needs no more, no less.&lt;/li&gt;

&lt;li id="5a7c"&gt;Runs perfectly on any Kubernetes cluster.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="b5f7"&gt;Perfect for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="1786"&gt;developers battling with resource tuning&lt;/li&gt;

&lt;li id="6033"&gt;Optimizing CPU/memory allocation&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="1b5b"&gt;🔗 &lt;a href="https://github.com/FairwindsOps/goldilocks" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Goldilocks GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2ABTNw5XV14Zk3eNJ6TuGsFQ.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="16eb"&gt;StormForge: Performance Optimisation Based on Artificial Intelligence&lt;/h2&gt;
&lt;h3 id="9231"&gt;The nature is:&lt;/h3&gt;
&lt;p id="9333"&gt;StormForge analyzes Kubernetes workloads using machine learning then suggests the most effective resource allocation.&lt;/p&gt;
&lt;h3 id="4a49"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="c630"&gt;Load testing powered by artificial intelligence discovers performance limits.&lt;/li&gt;

&lt;li id="2324"&gt;guarantees perfect running without over-provisioning.&lt;/li&gt;

&lt;li id="8077"&gt;aids in teams’ best use of cloud resources.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="1819"&gt;Perfect for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="eb13"&gt;High-performance Kubernetes applications&lt;/li&gt;

&lt;li id="7417"&gt;Teams driven by cost- consciousness&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="ceff"&gt;🔗 &lt;a href="https://www.stormforge.io/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;StormForge Website&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="5ebf"&gt;CAST AI: Optimal Kubernetes Automaton&lt;/h2&gt;
&lt;h3 id="9505"&gt;What it is depends on&lt;/h3&gt;
&lt;p id="412b"&gt;CAST AI completely automates Kubernetes optimization, therefore lowering cloud costs and enhancing performance free from human involvement.&lt;/p&gt;
&lt;h3 id="d46a"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="d818"&gt;cost savings driven by artificial intelligence, usually 50%+ cut in cloud expenses.&lt;/li&gt;

&lt;li id="5077"&gt;Depending on real-time use, automatically changes cluster size.&lt;/li&gt;

&lt;li id="0ce1"&gt;supports Kubernetes clusters spread over several clouds.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="43a7"&gt;Perfect for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="0a12"&gt;cost control of clouds&lt;/li&gt;

&lt;li id="9368"&gt;Kubernetes scaling automatedly&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="b9a6"&gt;🔗 &lt;a href="https://cast.ai/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;CAST AI&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="084e"&gt;Kepler: Kubernetes Energy Efficiency Driven by AI&lt;/h2&gt;
&lt;h3 id="5460"&gt;what it is:&lt;/h3&gt;
&lt;p id="9ea8"&gt;An artificial intelligence program called Kepler (based on Efficient Power Level Exporter) maximizes Kubernetes power use, hence lowering energy waste.&lt;/p&gt;
&lt;h3 id="3c90"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="f5ee"&gt;lowers carbon footprint by best use of energy sources.&lt;/li&gt;

&lt;li id="6c47"&gt;By tracking pod power use, helps green computing projects.&lt;/li&gt;

&lt;li id="d77e"&gt;increases big Kubernetes cluster cost effectiveness.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="135b"&gt;Greatest for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="597f"&gt;sustainable computing&lt;/li&gt;

&lt;li id="b0e0"&gt;Cost-efficient Kubernetes operations&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="23e2"&gt;🔗 &lt;a href="https://github.com/sustainable-computing-io/kepler" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Kepler GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="dc6b"&gt;Carpenter: Modern Kubernetes Autoscaling&lt;/h2&gt;
&lt;h3 id="f277"&gt;What is it?&lt;/h3&gt;
&lt;p id="c78e"&gt;Modern Kubernetes node autoscaler Carpenter uses ML models to provide better scaling choices.&lt;/p&gt;
&lt;h3 id="dc5c"&gt;Why is Kubernetes optimization perfect?&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="188e"&gt;faster autoscaling than standard instruments.&lt;/li&gt;

&lt;li id="3a47"&gt;Artificial intelligence-powered forecasts guarantee seamless workload scaling.&lt;/li&gt;

&lt;li id="f4a9"&gt;Designed for multi-cloud and AWS configurations.&lt;/li&gt;

&lt;/ul&gt;
&lt;h3 id="f91a"&gt;Ideal for:&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="e0ab"&gt;AI-driven autoscaling for cloud workloads&lt;/li&gt;

&lt;li id="bfd3"&gt;Next-generation Kubernetes scaling&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="33c2"&gt;🔗 &lt;a href="https://github.com/aws/karpenter" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Carpenter GitHub&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="2f07"&gt;Finally, consider this:&lt;/h2&gt;
&lt;p id="d2f6"&gt;Kubernetes isn’t has to be costly or ineffective. AI-powered products include Kubeflow, KEDA, VPA, and CAST AI simplify resource optimization, cloud cost reduction, and performance enhancement free from human labor.&lt;/p&gt;
&lt;p id="174c"&gt;Select the correct tool for your requirements, include it into your Kubernetes configuration, and let artificial intelligence manage the heavy work.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="5078"&gt;Want additional knowledge? Start optimizing your Kubernetes clusters right now by reviewing the official docs for every tool! 🚀&lt;/p&gt;&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>AI made me lazy. I didn’t notice until it was too late.</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Wed, 22 Apr 2026 12:45:01 +0000</pubDate>
      <link>https://forem.com/dev_tips/ai-made-me-lazy-i-didnt-notice-until-it-was-too-late-4kaa</link>
      <guid>https://forem.com/dev_tips/ai-made-me-lazy-i-didnt-notice-until-it-was-too-late-4kaa</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="4cab"&gt;&lt;/h1&gt;
&lt;h2 id="ec0f"&gt;&lt;em&gt;I used to actually think through problems. Now I just ask. Here’s what that’s doing to my brain and whether “lazy” is even the right word.&lt;/em&gt;&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A7pazm9QBtwvdoH9a-YGBvA.jpeg"&gt;&lt;p id="400a"&gt;There’s a specific kind of shame that hits when you’re staring at a for-loop you’ve written a thousand times and you’re waiting. Just sitting there, cursor blinking, waiting for Copilot to finish the sentence for you. Not because you don’t know how. You absolutely know how. You just… stopped bothering.&lt;/p&gt;
&lt;p id="8f73"&gt;That was me, sometime last year. I didn’t notice it happening. One day I was the guy who could rattle off a bash one-liner from memory. The next I was typing half a function name into chat and leaning back like I’d already done the hard part.&lt;/p&gt;
&lt;p id="576c"&gt;The git history doesn’t lie. I ran a diff on my own commits over the past year. The code is cleaner, ships faster, has fewer dumb typos. Also and this is the uncomfortable part I couldn’t explain maybe 30% of it line by line without reading it fresh. It’s my code. I just don’t fully &lt;em&gt;own&lt;/em&gt; it anymore.&lt;/p&gt;
&lt;p id="bc19"&gt;This isn’t a “AI bad, go back to Vim and suffering” article. I’m not about to tell you to delete Cursor and rediscover yourself through pointer arithmetic. But there’s something real happening to the way developers think, retain, and problem-solve and most of the discourse around it is either full doomer panic or Silicon Valley cope.&lt;/p&gt;
&lt;p id="0481"&gt;The honest answer is somewhere in the middle, and it’s weirder than both camps want to admit.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="5119"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AI tooling is quietly rewiring how developers build mental models. The output is going up. The comprehension is quietly going somewhere else. This piece breaks down what that actually looks like, why your brain is doing it on purpose, where the real trap is, and how to stay sharp without quitting the tools that genuinely make you faster.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="78e7"&gt;What lazy actually looks like now&lt;/h2&gt;
&lt;p id="c8c6"&gt;Before you dismiss this as another “kids these days” rant from someone who misses Stack Overflow, let me be specific. Because lazy in 2025 doesn’t look like slacking. It doesn’t look like a developer zoning out or skipping work. It looks like a really smooth, productive day where you shipped three features and closed six tickets and at the end of it, you couldn’t teach anyone what you actually did.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="8e27"&gt;That’s the new shape of lazy. It’s invisible. Your manager loves it.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="a2e9"&gt;The old dev loop had friction baked in. You’d hit a problem, Google it badly the first time, get irrelevant results, refine the search, land on a Stack Overflow thread from 2013 with a snarky comment and one upvoted answer that was &lt;em&gt;almost&lt;/em&gt; right, read the docs link someone buried in the third reply, actually understand the underlying mechanic, and then write the solution. That whole process felt like a waste of 45 minutes. It wasn’t. That was the part where you learned things.&lt;/p&gt;
&lt;p id="8859"&gt;The new loop is: describe problem, get solution, skim it, ship it. Four steps instead of nine. Objectively faster. Also missing the step where anything sticks.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AoLh8MvMkmB9TZhqe9V3FdQ.jpeg"&gt;&lt;p id="c7f1"&gt;Think about what GPS did to your sense of direction. You used to build a mental map of a city after a few trips turns, landmarks, rough distances. Now you follow a blue line and arrive somewhere with zero idea how you got there. Your brain didn’t get dumber. It just stopped bothering to store information it knew the phone would handle. That’s not a character flaw. That’s efficiency. The problem shows up the moment the GPS fails and you’re standing in an unfamiliar neighborhood with no internal model to fall back on.&lt;/p&gt;
&lt;p id="bc91"&gt;AI is doing the same thing to debugging instinct. To pattern recognition. To the quiet voice in the back of your head that says “wait, this feels like a race condition” before you can even articulate why.&lt;/p&gt;
&lt;p id="345b"&gt;I had a moment a few months back where a service started throwing intermittent errors in prod. Nothing obvious. The kind of thing that used to make me sit down, read logs slowly, form a hypothesis, test it, revise it the whole loop. Instead I pasted the error into chat, got three possible causes, picked the most plausible one, and deployed a fix. It worked. But I still don’t actually know which of those three causes was real. I closed the incident. I learned nothing. On to the next ticket.&lt;/p&gt;
&lt;p id="c2b2"&gt;That’s not a win. That’s a quiet debt accumulating in the background.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="bb18"&gt;Your brain is outsourcing, and it’s very efficient at it&lt;/h2&gt;
&lt;p id="15df"&gt;Here’s the part where I stop being dramatic and actually explain what’s happening because “AI is making us dumb” is a lazy take, and the real story is more interesting and more unsettling at the same time.&lt;/p&gt;
&lt;p id="3c98"&gt;What you’re experiencing has a name: cognitive offloading. It’s the completely normal, completely human behavior of using external tools to handle what your brain used to carry internally. You do it with calendars so you don’t memorize schedules. You do it with calculators so you don’t do long division in your head. You do it with Google so you don’t retain every fact you’ve ever needed once. None of that made you stupid. It freed up mental bandwidth for higher-order thinking or at least that’s the optimistic framing.&lt;/p&gt;
&lt;p id="afda"&gt;The brain is ruthlessly efficient. It prunes what it doesn’t use. This isn’t a metaphor it’s literal neuroscience. Neural pathways that go unused weaken over time. The mental muscle for grinding through a problem from first principles, for holding a complex system model in your head across a long debugging session, for pattern-matching errors you’ve seen before all of that requires regular use to stay sharp. Your brain doesn’t care that it’s important. It just notices you haven’t needed it in a while.&lt;/p&gt;
&lt;p id="89a0"&gt;The critical distinction and this is where most of the discourse gets sloppy is the difference between offloading a &lt;em&gt;tool&lt;/em&gt; and offloading your &lt;em&gt;judgment&lt;/em&gt;. Using a calculator to multiply large numbers is offloading a tool. Letting the calculator decide &lt;em&gt;which&lt;/em&gt; numbers to multiply is something else entirely. One frees you up. The other hollows you out.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AX6YBVaeNxOzPThjGPEm9og.jpeg"&gt;&lt;p id="c6ac"&gt;The calculator analogy only goes so far though, because AI isn’t just doing the arithmetic. It’s making architectural suggestions. It’s writing the logic. It’s deciding on the approach. And if you’re not actively interrogating those decisions if you’re just nodding along because the output looks clean and the tests pass you’re not offloading a tool. You’re offloading your engineering instinct.&lt;/p&gt;
&lt;p id="3835"&gt;There’s a great line that floats around in cognitive science circles: &lt;em&gt;“The tool shapes the user.”&lt;/em&gt; Hammers made us think in terms of force and impact. Spreadsheets made us think in rows and columns. AI assistants are training us, quietly, to think in prompts and responses to frame every problem as something you describe and receive an answer to, rather than something you sit with, decompose, and work through. That shift is subtle until it isn’t.&lt;/p&gt;
&lt;p id="d89d"&gt;My personal tell was regex. I used to write regex from memory not flexing, just a thing you pick up over years of wrangling text. One day I caught myself asking Claude for a basic email validation pattern. I knew I knew it. I just didn’t reach for it. The mental reflex had gone quiet from disuse, and grabbing the AI felt faster than waiting for it to wake up. That’s the moment I realized the offloading had gotten past tools and was starting to eat into instinct.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="6155"&gt;The productivity trap&lt;/h2&gt;
&lt;p id="6e1e"&gt;Let’s talk about the thing nobody says out loud in stand-up.&lt;/p&gt;
&lt;p id="5fa9"&gt;Your velocity is up. Your PR count is up. Your ticket close rate is up. You look, on every metric your team actually tracks, like you’re operating at a higher level than you were eighteen months ago. And in a real, measurable sense you are. The output is genuinely better. The code is cleaner, the documentation writes itself, the boilerplate that used to eat your mornings disappears in seconds.&lt;/p&gt;
&lt;p id="2584"&gt;The trap is that “output” and “understanding” are getting decoupled, and the gap between them is invisible right up until it isn’t.&lt;/p&gt;
&lt;p id="df36"&gt;Here’s what that looks like in practice. A junior developer in 2025 can produce code that reads like it was written by someone with five years of experience. The architecture looks considered. The variable names are sensible. The error handling is there. It passes review because it looks right. Then something breaks in a way the AI didn’t anticipate an edge case, a weird interaction with a legacy system, a race condition under load and suddenly that junior dev has no mental model to debug from. They didn’t build the thing. They assembled it. There’s a difference, and production doesn’t care which one you did.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AGCwQyBjzBLWj2e1YYhZmOg.jpeg"&gt;&lt;p id="5b6f"&gt;Andrej Karpathy coined the term “vibe coding” earlier this year and the dev community had very strong feelings about it because it named something everyone was already doing and pretending wasn’t happening. You describe what you want, you accept what looks right, you run it until it works. The vibes are good. The understanding is optional. Karpathy’s original post was half-joking, but the replies were full of people either defensively laughing or quietly going “oh no.”&lt;/p&gt;
&lt;p id="f6f0"&gt;The more experienced you are, the safer AI tools are in your hands not because seniors are smarter, but because they have the underlying mental models to catch when the AI is wrong. A senior engineer who’s debugged enough distributed systems to feel a race condition in their bones can use AI to go faster without losing their footing. They’re using it like a power tool. A developer who learned to code in the AI era is sometimes using it more like a crutch and the difference only shows up when the crutch gets kicked out.&lt;/p&gt;
&lt;p id="885c"&gt;I’m not blaming anyone for this. The incentives are completely stacked toward shipping fast. Nobody is rewarding you for taking the long route to deeply understand a concept when the short route produces the same ticket closure. The trap isn’t stupidity. It’s rationality operating in a system with the wrong reward signals.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="4f96"&gt;But here’s the question worth sitting with: if you can’t explain what the code does in plain English, did you actually write it? And more importantly when it breaks at the worst possible time, are you the person who can fix it, or are you the person who pastes the error back into chat and hopes?&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="133d"&gt;How to stay sharp without going full caveman&lt;/h2&gt;
&lt;p id="1921"&gt;So what’s the actual fix? Because “just stop using AI” is the equivalent of telling someone to delete Google Maps and buy a paper atlas. It’s not happening, it shouldn’t happen, and anyone telling you to do it probably also thinks dark mode is a personality.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="d279"&gt;The answer isn’t less AI. It’s deliberate friction.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="1d93"&gt;Athletes at the highest level still drill fundamentals. A professional basketball player who has access to film analysis, biometric tracking, and every performance metric imaginable still stands at the free-throw line and does repetitions. Not because the tech isn’t helping it absolutely is but because the underlying physical pattern needs to stay grooved. You can’t analytics your way out of not having practiced the shot. The same principle applies to engineering instincts. The AI is the analytics suite. You still need to take the shots.&lt;/p&gt;
&lt;p id="884e"&gt;The most useful habit I’ve built and it’s embarrassingly simple is asking AI to explain before it generates. Instead of “write me a function that does X,” the prompt becomes “explain how you’d approach X, then write it.” That one shift forces the output to be legible to you before you accept it. You can’t just skim and ship. You have to follow the reasoning, and following reasoning is the part that builds mental models. It’s slower by maybe two minutes. It’s worth it by a lot.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AK_54-8ugpVMyLtnCxupsHA.jpeg"&gt;&lt;p id="0377"&gt;The second thing is bringing back intentional no-AI problems. Not a full detox, just regular reps. Once a week, pick something small a script, a data transform, a tricky query and work through it with just the docs. Not because the AI wouldn’t do it faster. It obviously would. But because the friction is the point. Exercism.org is genuinely good for this structured practice problems across languages, no AI dependency required, just you and the problem. Think of it as leg day. Nobody enjoys leg day. Everyone who skips it regrets it eventually.&lt;/p&gt;
&lt;p id="c11c"&gt;The third thing is reading your own AI-generated code like it’s a PR from someone you don’t fully trust yet. Because that’s what it is. Not hostile review just genuine engagement. Ask yourself if you could rewrite the core logic from scratch if you had to. If the answer is no, you haven’t finished the job. The AI drafted it. You still have to own it.&lt;/p&gt;
&lt;p id="c3d4"&gt;None of this is about moral purity or proving you’re a “real developer” by doing everything the hard way. That conversation is tired. This is purely practical the devs who’ll have the most resilience over the next five years aren’t the ones who used AI the most or the least. They’re the ones who kept their mental models intact while using AI to move faster. The tool is genuinely incredible. The goal is to stay the person holding it, not the person being carried by it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="f696"&gt;Lazy is fine. Hollow is not.&lt;/h2&gt;
&lt;p id="bdc1"&gt;Here’s where I land on this, after a year of watching my own git history and being uncomfortably honest about what it says.&lt;/p&gt;
&lt;p id="fa06"&gt;AI didn’t make me a worse developer. But it’s been quietly making offers to take things off my plate that I probably shouldn’t have handed over and I said yes more often than I should have, because the short-term math always worked out. Faster ticket. Cleaner code. Happy manager. The long-term math is still being written.&lt;/p&gt;
&lt;p id="6590"&gt;The “AI will make developers lazy” discourse has been loud and mostly useless because it frames this as a character problem. It isn’t. It’s an incentive problem, a habit problem, and a slightly uncomfortable neuroscience problem. Your brain is doing exactly what brains do optimizing for efficiency in the current environment. The environment just changed faster than anyone built good habits for.&lt;/p&gt;
&lt;p id="8774"&gt;The calculator crowd won the argument, by the way. Nobody seriously thinks students shouldn’t use calculators anymore. But the mathematicians who understood what was happening underneath the arithmetic built things the calculator-dependent ones couldn’t. That gap is coming for software engineering too, just slower and less obviously.&lt;/p&gt;
&lt;p id="acd3"&gt;My actual prediction and you can quote me on this when it ages badly is that the most valuable developers in five years won’t be the ones who prompt the best. It’ll be the ones who know when the AI is wrong, why it’s wrong, and how to fix it without asking the AI to fix itself. That skill requires mental models. Mental models require use. Use requires occasionally doing the hard thing on purpose.&lt;/p&gt;
&lt;p id="87fe"&gt;The git history doesn’t lie. Make sure yours is telling a story you actually understand.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="4e34"&gt;&lt;em&gt;What’s your lazy tell? The thing you caught yourself offloading that you probably shouldn’t have? Drop it in the comments genuinely curious where everyone’s line is.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="0d60"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="7c22"&gt;

&lt;a href="https://exercism.org/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Exercism.org&lt;/strong&gt;&lt;/a&gt; structured deliberate practice, no AI required&lt;/li&gt;

&lt;li id="2885"&gt;

&lt;a href="https://pragprog.com/titles/ahptl/pragmatic-thinking-and-learning/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Pragmatic Thinking and Learning Andy Hunt&lt;/strong&gt;&lt;/a&gt; still the best book on how developer brains actually work&lt;/li&gt;

&lt;li id="7c90"&gt;

&lt;a href="https://www.sciencedirect.com/science/article/pii/S2352250X22000264" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Cognitive offloading research ScienceDirect&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;if you want the actual science behind why this happens&lt;/li&gt;

&lt;li id="5f6d"&gt;

&lt;a href="https://www.reddit.com/r/ExperiencedDevs/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;r/ExperiencedDevs AI dependency thread&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;real dev takes, no LinkedIn polish&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I replaced my entire backend team with Claude Code for 30 days day 15 was a disaster</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Tue, 21 Apr 2026 07:47:23 +0000</pubDate>
      <link>https://forem.com/dev_tips/i-replaced-my-entire-backend-team-with-claude-code-for-30-days-day-15-was-a-disaster-3b8p</link>
      <guid>https://forem.com/dev_tips/i-replaced-my-entire-backend-team-with-claude-code-for-30-days-day-15-was-a-disaster-3b8p</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="89c3"&gt;&lt;strong&gt;30 days. One AI. One very bad Tuesday.&lt;/strong&gt;&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p id="4e48"&gt;Okay, I didn’t actually fire anyone. Let me be honest before the pitchforks come out. I’m a solo dev. There was no team. But I did set a hard rule for 30 days: every piece of backend code schemas, auth, API routes, migrations, the works had to go through Claude Code first. No copy-pasting from Stack Overflow, no reaching for my old project templates, no “I’ll just write this one quick.” If it touched the backend, the AI had to touch it first.&lt;/p&gt;
&lt;p id="bfd6"&gt;I’d been watching the discourse for months. Half of dev Twitter is “AI will replace engineers.” The other half is “Claude wrote me a bubble sort with a memory leak, these tools are toys.” Both camps are annoying. Both camps are also partially right, which is the actual interesting thing. So I ran the experiment myself, on a real project a scheduling API for a client with real stakes and a real deadline.&lt;/p&gt;
&lt;p id="b668"&gt;The first week felt like a cheat code. The second week felt like pair programming with someone brilliant but slightly unhinged. Day 15 felt like watching a confident intern delete a production table and explain, calmly, why it was actually fine.&lt;/p&gt;
&lt;p id="ee17"&gt;This isn’t a “Claude Code bad” piece. It’s also not a hype piece. It’s a field report from &lt;strong&gt;30 days&lt;/strong&gt; of actually using it as your primary backend dev the parts that worked embarrassingly well, the part that nearly tanked the project, and the mental model shift that changed how I use AI tools permanently.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="98ca"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Claude Code is genuinely fast and good at the boring 60% of backend work. It also has a specific failure mode that isn’t obvious until something breaks. Once you understand that failure mode, the whole game changes.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="d1bc"&gt;The setup&lt;/h2&gt;
&lt;p id="1167"&gt;The project was a scheduling API for a small logistics client Node.js, Express, PostgreSQL, nothing exotic. The kind of backend a mid-level dev could scaffold in a weekend if they weren’t overthinking it. Three main entities: users, jobs, time slots. Auth via JWT. A handful of endpoints. Boring on purpose I didn’t want the stack to be the variable, I wanted the AI to be the variable.&lt;/p&gt;
&lt;p id="a576"&gt;&lt;strong&gt;The rules I set for myself were simple and deliberately uncomfortable:&lt;/strong&gt;&lt;/p&gt;
&lt;p id="5014"&gt;No writing backend code from scratch. Every function, every migration, every middleware Claude Code drafts it first, I review and ship. If I disagreed with the output, I could edit it, but I had to articulate why, like I was reviewing a junior dev’s PR. No silent rewrites.&lt;/p&gt;
&lt;p id="939c"&gt;No reaching for my snippet library. I have a folder of auth boilerplate I’ve reused across four projects. Completely off limits. Claude had to build it fresh every time.&lt;/p&gt;
&lt;p id="5d3e"&gt;I logged every session. Time to first working output, number of back-and-forths, any bugs I caught before merging. Thirty days of notes in a markdown file that I did not expect to become an article.&lt;/p&gt;
&lt;p id="3f5b"&gt;Going in, my expectations were calibrated somewhere between “this saves me &lt;strong&gt;20%&lt;/strong&gt; of time” and “this is actually kind of wild.” I’d used Claude in chat before for debugging and explaining concepts. Claude Code felt different from the first session it’s not autocomplete, it’s not a chatbot, it’s closer to dropping a context bomb on a capable dev and watching them run with it. Whether that’s good or terrifying depends entirely on day 15.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2Ats0ukpY9PEK1vMEauCIDkA.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="3846"&gt;When it actually worked&lt;/h2&gt;
&lt;p id="4c7c"&gt;The first thing Claude Code demolished was auth. I gave it the schema, told it JWT, refresh tokens, role-based access, and walked away to make coffee. Came back to a working implementation middleware, token rotation logic, the whole thing. Not perfect, but 85% there on the first pass. Normally that’s a two-hour job minimum, the kind where you’re tabbing between the docs, your last project, and a Stack Overflow thread from 2019 that’s somehow still the top result.&lt;/p&gt;
&lt;p id="3021"&gt;&lt;strong&gt;Day 3&lt;/strong&gt; was the moment I started taking notes. I needed a database migration new table, foreign keys, indexes, the usual friction. Described the relationship in plain English: “jobs belong to users, time slots belong to jobs, cascade deletes on both.” Claude Code wrote the migration, the rollback, and flagged a potential index I’d missed on the time slot lookup query. Forty minutes start to finish, including me reading through it carefully. That same task on my last project took most of an afternoon because I kept second-guessing the cascade behavior and went down a Postgres docs rabbit hole.&lt;/p&gt;
&lt;p id="fef6"&gt;The pattern held for the first ten days. CRUD endpoints, input validation, error handling middleware all the scaffolding work that’s not hard but is relentlessly tedious Claude Code handled it faster than I could have, and cleaner than I usually bother with when I’m trying to hit a deadline. It wrote tests I wouldn’t have written until the end. It added logging I’d have skipped until something broke in production.&lt;/p&gt;
&lt;p id="e3fc"&gt;The honest thing to say here is: for well-defined, self-contained backend tasks, it’s not slightly better than doing it yourself. It’s embarrassingly better. The constraint is the word “self-contained.” That constraint matters a lot. You’ll see why on &lt;strong&gt;day 15&lt;/strong&gt;.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2Ax1BJ7IQBOXwRWe0p4sidKg.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="0e94"&gt;Day 15 was a disaster&lt;/h2&gt;
&lt;p id="1523"&gt;Two weeks in, I was feeling dangerous. The project was ahead of schedule, the code was clean, and I’d started telling people about the experiment with the energy of someone who just discovered a life hack. Classic mistake. The universe has a specific punishment reserved for developers who get comfortable.&lt;/p&gt;
&lt;p id="009a"&gt;Day 15 I needed to add a job reassignment feature move a job from one user to another, update the related time slots, fire a notification. Interconnected logic across three tables. I’d been feeding Claude Code individual files and focused prompts the whole time, but this felt straightforward enough. I dumped the relevant models, described the feature, and let it run.&lt;/p&gt;
&lt;p id="95fc"&gt;It wrote confident, clean-looking code. It always writes confident, clean-looking code. That’s part of the problem.&lt;/p&gt;
&lt;p id="55da"&gt;What it didn’t know because I hadn’t told it, because it felt obvious to me was that we’d added a soft-delete pattern to the time slots table on day 9. A small schema change I’d made in a separate session, never referenced again. Claude Code had no memory of that session. It wrote the reassignment logic against the table structure it knew from day 1, which meant the cascade update silently skipped soft-deleted rows. No error. No warning. Just wrong data, wearing the face of correct data.&lt;/p&gt;
&lt;p id="9be6"&gt;I caught it in review, barely. A Hacker News thread from around the same time had a comment that stuck with me someone described Claude Code as&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="8bc2"&gt;&lt;em&gt;“a brilliant contractor who only knows what’s in the folder you hand them.”&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="dac6"&gt;That’s exactly it. The problem wasn’t the AI. The problem was I’d stopped treating it like a contractor and started treating it like a teammate with shared context.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="a6a6"&gt;“The moment you forget it has no memory of yesterday’s session, you’ve already made the mistake.” dev on r/ClaudeAI, which I read approximately one day too late.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="fcac"&gt;The fix took twenty minutes. The lesson took longer to fully land.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A0NgswqKns1nsXlUeyEgtOw.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="41bb"&gt;What I actually learned&lt;/h2&gt;
&lt;p id="bbfb"&gt;The mental model most people bring to AI coding tools is wrong, and it’s wrong in a specific direction. They either treat it like a search engine that writes code disposable, low-trust, double-check everything — or they treat it like a senior engineer who’s got the full picture. Day 15 exists in the gap between those two mental models.&lt;/p&gt;
&lt;p id="bf0a"&gt;The framing that actually worked for me, after 30 days, is this: Claude Code is a senior intern. Technically sharp, genuinely fast, capable of producing work that makes you look good. But it only knows what you’ve explicitly handed it, it has no institutional memory, and it will never tell you it’s missing context. It’ll just fill the gap with a confident assumption and keep moving. Sound like anyone you’ve hired?&lt;/p&gt;
&lt;p id="ee81"&gt;The practical shift that came out of that is boring but it works. Start every non-trivial session with a context dump. Not a vague “here’s the project” intro a tight, specific brief: current schema, recent changes, any decisions made in previous sessions that affect this one. Treat it like onboarding a contractor for a single day. What does this person need to know right now to not accidentally wreck something?&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="1ef6"&gt;“Prompting is just architecture with different syntax. Garbage in, garbage out same as always.” from a dev blog I’ve since lost the link to, but the line stuck.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="3d4f"&gt;The other thing and this one’s uncomfortable is that the quality of the output is tightly coupled to the quality of your thinking going in. When Claude Code produced clean, solid work in weeks one and two, it wasn’t just because the tool is good. It was because I gave it clean, well-scoped problems. The day 15 failure wasn’t really a Claude Code failure. It was a me failure dressed up as an AI failure. I got lazy with the brief because things had been going well.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AApaBYRxXUtC2kAFbrFT3Gg.jpeg"&gt;&lt;p id="36db"&gt;The devs I’ve seen get the most out of these tools aren’t the ones who trust it most. They’re the ones who’ve built the tightest review habits around it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="be4a"&gt;The verdict&lt;/h2&gt;
&lt;blockquote&gt;&lt;p id="f184"&gt;Would I do it again? Yeah, without hesitation. Would I do it the same way? Absolutely not.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="7016"&gt;Thirty days in, the number that surprised me most wasn’t the time saved on boilerplate I expected that. It was how much my own thinking sharpened. Writing tight context briefs every session, scoping problems cleanly before handing them off, reviewing output like a PR instead of skimming it those habits made me a better engineer, not a lazier one. That’s not the narrative people expect from an “I replaced my team with AI” piece, but it’s what actually happened.&lt;/p&gt;
&lt;p id="e47e"&gt;The current discourse around AI coding tools is stuck in a binary that doesn’t map to reality. It’s not “AI replaces developers” vs “AI is a glorified autocomplete.” The real story is more interesting and more demanding: AI tools compress the time between idea and working code so aggressively that the bottleneck shifts. It’s not typing speed anymore. It’s not even knowing syntax. It’s thinking clearly, scoping well, and reviewing ruthlessly. The developers who struggle with AI tools are usually the ones who were coasting on the execution layer and hadn’t noticed.&lt;/p&gt;
&lt;p id="c505"&gt;Day 15 was a disaster because I got sloppy. Days 1 through 14 and 16 through 30 were, genuinely, some of the most productive backend work I’ve shipped. That ratio feels about right for where these tools are in 2025 powerful enough to change how you work, rough enough around the edges to punish you when you stop paying attention.&lt;/p&gt;
&lt;p id="658f"&gt;The backend team didn’t get replaced. It got compressed into one dev with better tools and slightly worse sleep. Whether that’s exciting or terrifying probably says more about where you sit in the org chart than anything else.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AMqik4aYfgWoyFEa_z9amFg.jpeg"&gt;&lt;blockquote&gt;&lt;p id="0bf3"&gt;Drop your day 15 story in the comments. I know you have one.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="3cc0"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="907f"&gt;

&lt;a href="https://docs.anthropic.com/en/docs/claude-code/overview" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Claude Code documentation&lt;/strong&gt;&lt;/a&gt; official setup, capabilities, and best practices&lt;/li&gt;

&lt;li id="1f1b"&gt;

&lt;a href="https://www.npmjs.com/package/@anthropic-ai/claude-code" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Claude Code on npm&lt;/strong&gt;&lt;/a&gt; installation and version history&lt;/li&gt;

&lt;li id="ffe8"&gt;

&lt;a href="https://www.reddit.com/r/ClaudeAI/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;r/ClaudeAI&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;community threads, real-world usage patterns, war stories&lt;/li&gt;

&lt;li id="ba9f"&gt;

&lt;a href="https://news.ycombinator.com/item?id=40959428" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Hacker News “Ask HN: How are you using Claude Code in production?”&lt;/strong&gt;&lt;/a&gt; worth reading before you start&lt;/li&gt;

&lt;li id="dcc8"&gt;

&lt;a href="https://expressjs.com/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Express.js docs&lt;/strong&gt;&lt;/a&gt; still the most reliable Node backend reference&lt;/li&gt;

&lt;li id="1e2c"&gt;

&lt;a href="https://node-postgres.com/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;node-postgres (pg) docs&lt;/strong&gt;&lt;/a&gt; if you’re on Postgres, keep this open&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>coding</category>
      <category>claude</category>
    </item>
    <item>
      <title>I read 50 Python library lists so you don’t have to here are the 20 that actually matter</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Tue, 21 Apr 2026 07:43:11 +0000</pubDate>
      <link>https://forem.com/dev_tips/i-read-50-python-library-lists-so-you-dont-have-to-here-are-the-20-that-actually-matter-47a4</link>
      <guid>https://forem.com/dev_tips/i-read-50-python-library-lists-so-you-dont-have-to-here-are-the-20-that-actually-matter-47a4</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="1c3e"&gt;You’re still using Pandas and pip. Your competitors aren’t. Here are the 20 libraries reshaping how real Python gets written with code, docs, and zero filler.&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p id="0cc5"&gt;Every six months, someone publishes another “top Python libraries” listicle. You skim it, recognize seven tools you already use, close the tab, and go back to your &lt;code&gt;requirements.txt&lt;/code&gt; that hasn't changed since 2021.&lt;/p&gt;
&lt;p id="c9ae"&gt;&lt;strong&gt;This isn’t that article.&lt;/strong&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A5Vopjj6_c-pEGAJFMfnHOQ.jpeg"&gt;&lt;p id="c8a2"&gt;Python is 34 years old and somehow keeps accelerating. The reason isn’t the syntax. It isn’t even the community. It’s the library culture developers who looked at slow tools, shrugged, and rewrote them in Rust over a weekend. The result is a 2026 Python ecosystem that looks almost nothing like what most tutorials still teach.&lt;/p&gt;
&lt;p id="2ce3"&gt;The uncomfortable truth is that a lot of working developers are running stacks built on decade-old assumptions. Pandas where Polars would be 50x faster. &lt;code&gt;pip&lt;/code&gt; where &lt;code&gt;uv&lt;/code&gt; would save them minutes every single day. &lt;code&gt;requests&lt;/code&gt; where &lt;code&gt;httpx&lt;/code&gt; handles async without a second thought. Nobody told them. No single article had all of it in one place.&lt;/p&gt;
&lt;p id="496e"&gt;I got tired of that. So I went through 50 lists, filtered out the sponsored picks, the recycled classics, and the stuff that’s been on every article since 2018 and kept only what’s actually worth your time in 2026. Twenty libraries. Five categories. Every one gets a real code example, a straight “why you should care,” and a direct docs link.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="c0f3"&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; 20 libraries across five categories, no filler, no padding, grouped by what you’re actually building.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="9e31"&gt;Data &amp;amp; performance&lt;/h2&gt;
&lt;p id="964b"&gt;Four libraries. All of them embarrass something you’re probably still using.&lt;/p&gt;
&lt;h3 id="a671"&gt;1. Polars: the DataFrame library that makes Pandas feel slow&lt;/h3&gt;
&lt;p id="40ce"&gt;Polars is a blazingly fast DataFrame library written in Rust. It’s not “the new Pandas.” It’s what Pandas would look like if it were designed today, by people who’d already made all the Pandas mistakes.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="8d78"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; 10x–100x faster than Pandas on most operations. Supports lazy evaluation so it only computes what it needs. Works natively with Apache Arrow. Parallelizes across all your CPU cores by default no extra config.&lt;/em&gt;&lt;/p&gt;

&lt;p id="a90e"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://docs.pola.rs/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;docs.pola.rs&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="bb62"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="0832"&gt;pip install polars&lt;/span&gt;&lt;/pre&gt;
&lt;p id="7753"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="484f"&gt;&lt;span&gt;import&lt;/span&gt; polars &lt;span&gt;as&lt;/span&gt; pl&lt;br&gt;&lt;br&gt;df = pl.read_csv(&lt;span&gt;"data.csv"&lt;/span&gt;)&lt;br&gt;&lt;br&gt;result = (&lt;br&gt;    df&lt;br&gt;    .&lt;span&gt;filter&lt;/span&gt;(pl.col(&lt;span&gt;"age"&lt;/span&gt;) &amp;gt; &lt;span&gt;25&lt;/span&gt;)&lt;br&gt;    .group_by(&lt;span&gt;"city"&lt;/span&gt;)&lt;br&gt;    .agg(pl.col(&lt;span&gt;"salary"&lt;/span&gt;).mean().alias(&lt;span&gt;"avg_salary"&lt;/span&gt;))&lt;br&gt;    .sort(&lt;span&gt;"avg_salary"&lt;/span&gt;, descending=&lt;span&gt;True&lt;/span&gt;)&lt;br&gt;)&lt;br&gt;&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(result)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="1413"&gt;I once ran a 4GB CSV through Pandas it took a few minutes. Same operation in Polars: 8 seconds. My manager thought I’d rewritten the query logic. I hadn’t touched it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="c9e2"&gt;2. DuckDB: SQL analytics without a server&lt;/h3&gt;
&lt;p id="e577"&gt;DuckDB is an in-process analytical database. Think SQLite, but built for OLAP workloads instead of transactional ones. No server. No config. Just fast SQL directly on your DataFrames, Parquet files, and CSVs.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="43a2"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Zero setup runs inside your Python process. Blazing fast for analytical queries. Reads Parquet, CSV, and Pandas/Polars DataFrames natively. Replaces a surprising amount of infrastructure.&lt;/em&gt;&lt;/p&gt;

&lt;p id="eeb9"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://duckdb.org/docs/stable/clients/python/overview.html" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;duckdb.org/docs&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="22f5"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="3022"&gt;pip install duckdb&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f6af"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="1872"&gt;&lt;span&gt;import&lt;/span&gt; duckdb&lt;br&gt;&lt;span&gt;import&lt;/span&gt; pandas &lt;span&gt;as&lt;/span&gt; pd&lt;br&gt;&lt;br&gt;df = pd.DataFrame({&lt;br&gt;    &lt;span&gt;"name"&lt;/span&gt;: [&lt;span&gt;"Alice"&lt;/span&gt;, &lt;span&gt;"Bob"&lt;/span&gt;, &lt;span&gt;"Charlie"&lt;/span&gt;],&lt;br&gt;    &lt;span&gt;"sales"&lt;/span&gt;: [&lt;span&gt;120&lt;/span&gt;, &lt;span&gt;340&lt;/span&gt;, &lt;span&gt;210&lt;/span&gt;]&lt;br&gt;})&lt;br&gt;&lt;br&gt;result = duckdb.sql(&lt;span&gt;"SELECT name, sales FROM df WHERE sales &amp;gt; 150 ORDER BY sales DESC"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(result)&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span id="5ca7"&gt;┌─────────┬───────┐&lt;br&gt;│  name   │ sales │&lt;br&gt;│ varchar │ &lt;span&gt;int64&lt;/span&gt; │&lt;br&gt;├─────────┼───────┤&lt;br&gt;│ Bob     │   &lt;span&gt;340&lt;/span&gt; │&lt;br&gt;│ Charlie │   &lt;span&gt;210&lt;/span&gt; │&lt;br&gt;└─────────┴───────┘&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9554"&gt;The fact that you can run SQL directly on a Pandas DataFrame without a database engine running anywhere is genuinely one of those things that feels like cheating.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="32a8"&gt;3. Pandera: Pydantic for your DataFrames&lt;/h3&gt;
&lt;p id="fedb"&gt;Pandera adds schema-based validation to Pandas and Polars DataFrames. If you’ve ever had a pipeline silently fail because a column changed type in production, this is the library that stops that from happening.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="1649"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Catches data errors before they reach your business logic. Works like Pydantic but for tabular data. Supports unit testing for data pipelines. Integrates with both Pandas and Polars.&lt;/em&gt;&lt;/p&gt;

&lt;p id="22c8"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://pandera.readthedocs.io/en/stable/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;pandera.readthedocs.io&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="1ccf"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="bd6e"&gt;pip install pandera&lt;/span&gt;&lt;/pre&gt;
&lt;p id="67e2"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="8c7c"&gt;&lt;span&gt;import&lt;/span&gt; pandas &lt;span&gt;as&lt;/span&gt; pd&lt;br&gt;&lt;span&gt;import&lt;/span&gt; pandera &lt;span&gt;as&lt;/span&gt; pa&lt;br&gt;&lt;br&gt;schema = pa.DataFrameSchema({&lt;br&gt;    &lt;span&gt;"user_id"&lt;/span&gt;: pa.Column(&lt;span&gt;int&lt;/span&gt;, checks=pa.Check.gt(&lt;span&gt;0&lt;/span&gt;)),&lt;br&gt;    &lt;span&gt;"score"&lt;/span&gt;: pa.Column(&lt;span&gt;float&lt;/span&gt;, checks=pa.Check.in_range(&lt;span&gt;0.0&lt;/span&gt;, &lt;span&gt;100.0&lt;/span&gt;)),&lt;br&gt;    &lt;span&gt;"status"&lt;/span&gt;: pa.Column(&lt;span&gt;str&lt;/span&gt;, checks=pa.Check.isin([&lt;span&gt;"active"&lt;/span&gt;, &lt;span&gt;"inactive"&lt;/span&gt;])),&lt;br&gt;})&lt;br&gt;&lt;br&gt;df = pd.DataFrame({&lt;br&gt;    &lt;span&gt;"user_id"&lt;/span&gt;: [&lt;span&gt;1&lt;/span&gt;, &lt;span&gt;2&lt;/span&gt;, &lt;span&gt;3&lt;/span&gt;],&lt;br&gt;    &lt;span&gt;"score"&lt;/span&gt;: [&lt;span&gt;85.5&lt;/span&gt;, &lt;span&gt;92.0&lt;/span&gt;, &lt;span&gt;73.3&lt;/span&gt;],&lt;br&gt;    &lt;span&gt;"status"&lt;/span&gt;: [&lt;span&gt;"active"&lt;/span&gt;, &lt;span&gt;"inactive"&lt;/span&gt;, &lt;span&gt;"active"&lt;/span&gt;]&lt;br&gt;})&lt;br&gt;&lt;br&gt;validated = schema(df)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(validated)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="152f"&gt;Bad data hits the schema, raises an error, and you fix it before it costs you a three-hour debugging session. That’s the whole pitch and it’s a good one.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="394" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AQnZPIPs1_q2WaPT6s9X1hQ.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="27da"&gt;4. PyArrow: the USB-C of Python data&lt;/h3&gt;
&lt;p id="8ab3"&gt;PyArrow is the Python interface to Apache Arrow, a columnar in-memory format that’s become the connective tissue of the modern data stack. You might not use it directly every day but Polars, DuckDB, and Pandas all run on top of it.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="ee5a"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Zero-copy memory sharing between tools. Reads and writes Parquet natively. Powers the data exchange layer between Polars, DuckDB, Pandas, and most modern data tools. Essential for building fast pipelines.&lt;/em&gt;&lt;/p&gt;

&lt;p id="8957"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://arrow.apache.org/docs/python/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;arrow.apache.org/docs/python&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="d94d"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f6a0"&gt;pip install pyarrow&lt;/span&gt;&lt;/pre&gt;
&lt;p id="1f5c"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="102f"&gt;&lt;span&gt;import&lt;/span&gt; pyarrow &lt;span&gt;as&lt;/span&gt; pa&lt;br&gt;&lt;span&gt;import&lt;/span&gt; pyarrow.parquet &lt;span&gt;as&lt;/span&gt; pq&lt;br&gt;&lt;br&gt;table = pa.table({&lt;br&gt;    &lt;span&gt;"id"&lt;/span&gt;: [&lt;span&gt;1&lt;/span&gt;, &lt;span&gt;2&lt;/span&gt;, &lt;span&gt;3&lt;/span&gt;],&lt;br&gt;    &lt;span&gt;"value"&lt;/span&gt;: [&lt;span&gt;10.5&lt;/span&gt;, &lt;span&gt;20.1&lt;/span&gt;, &lt;span&gt;30.9&lt;/span&gt;]&lt;br&gt;})&lt;br&gt;&lt;br&gt;&lt;span&gt;# Write to Parquet&lt;/span&gt;&lt;br&gt;pq.write_table(table, &lt;span&gt;"output.parquet"&lt;/span&gt;)&lt;br&gt;&lt;br&gt;&lt;span&gt;# Read back&lt;/span&gt;&lt;br&gt;loaded = pq.read_table(&lt;span&gt;"output.parquet"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(loaded)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="a069"&gt;You don’t need to deeply understand Arrow to benefit from it. But once your pipelines start talking to each other in Parquet instead of CSV, you’ll wonder how you shipped anything before.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="dffc"&gt;AI &amp;amp; LLM tooling&lt;/h2&gt;
&lt;p id="2412"&gt;The AI library space moves so fast that half the tutorials you read last year are already deprecated. These four have earned their place.&lt;/p&gt;
&lt;h3 id="f3e5"&gt;5. LlamaIndex: the RAG framework that actually makes sense&lt;/h3&gt;
&lt;p id="432c"&gt;LlamaIndex is the go-to framework for building Retrieval-Augmented Generation pipelines. If you want your LLM to answer questions about &lt;em&gt;your&lt;/em&gt; data your PDFs, your databases, your internal docs LlamaIndex is the cleanest way to get there.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="6006"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Purpose-built for RAG workflows. Connects to OpenAI, HuggingFace, Anthropic, and most major LLM providers. Handles document ingestion, chunking, indexing, and querying in one coherent API. Actively maintained with a massive plugin ecosystem.&lt;/em&gt;&lt;/p&gt;

&lt;p id="499d"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://docs.llamaindex.ai/en/stable/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;docs.llamaindex.ai&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="5fa1"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="a1c6"&gt;pip install llama-index&lt;/span&gt;&lt;/pre&gt;
&lt;p id="b480"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="1efa"&gt;&lt;span&gt;from&lt;/span&gt; llama_index.core &lt;span&gt;import&lt;/span&gt; VectorStoreIndex, SimpleDirectoryReader&lt;br&gt;&lt;span&gt;from&lt;/span&gt; llama_index.llms.openai &lt;span&gt;import&lt;/span&gt; OpenAI&lt;br&gt;&lt;br&gt;&lt;span&gt;# Load documents from a folder&lt;/span&gt;&lt;br&gt;documents = SimpleDirectoryReader(&lt;span&gt;"./docs"&lt;/span&gt;).load_data()&lt;br&gt;&lt;br&gt;&lt;span&gt;# Build an index&lt;/span&gt;&lt;br&gt;index = VectorStoreIndex.from_documents(documents)&lt;br&gt;&lt;br&gt;&lt;span&gt;# Query it&lt;/span&gt;&lt;br&gt;query_engine = index.as_query_engine()&lt;br&gt;response = query_engine.query(&lt;span&gt;"What is our refund policy?"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(response)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="b898"&gt;Three lines to load your docs, two to build an index, one to query. That’s the kind of API design that makes you actually want to build things.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="fc2a"&gt;6. LangChain: Lego blocks for AI agents&lt;/h3&gt;
&lt;p id="137f"&gt;LangChain is the framework for chaining LLM calls together with tools, memory, and external APIs. It’s opinionated, occasionally over-engineered, and still the most complete solution for building complex AI workflows in Python.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="a548"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Chains multiple LLM calls with logic between them. Supports memory so your agents remember context. Integrates with OpenAI, HuggingFace, Google, and more. Has a massive community and plugin library.&lt;/em&gt;&lt;/p&gt;

&lt;p id="a347"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://python.langchain.com/docs/introduction/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;python.langchain.com&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="0c82"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="866d"&gt;pip install langchain&lt;br&gt;pip install -qU &lt;span&gt;"langchain[openai]"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="83b7"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f65e"&gt;&lt;span&gt;import&lt;/span&gt; os&lt;br&gt;&lt;span&gt;from&lt;/span&gt; langchain.chat_models &lt;span&gt;import&lt;/span&gt; init_chat_model&lt;br&gt;&lt;span&gt;from&lt;/span&gt; langchain_core.messages &lt;span&gt;import&lt;/span&gt; HumanMessage, SystemMessage&lt;br&gt;&lt;br&gt;os.environ[&lt;span&gt;"OPENAI_API_KEY"&lt;/span&gt;] = &lt;span&gt;"your-key-here"&lt;/span&gt;&lt;br&gt;&lt;br&gt;model = init_chat_model(&lt;span&gt;"gpt-4o-mini"&lt;/span&gt;, model_provider=&lt;span&gt;"openai"&lt;/span&gt;)&lt;br&gt;&lt;br&gt;messages = [&lt;br&gt;    SystemMessage(&lt;span&gt;"You are a helpful Python tutor."&lt;/span&gt;),&lt;br&gt;    HumanMessage(&lt;span&gt;"Explain decorators in one paragraph."&lt;/span&gt;),&lt;br&gt;]&lt;br&gt;&lt;br&gt;response = model.invoke(messages)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(response.content)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9172"&gt;Is LangChain bloated? Sometimes. Does it still ship faster than rolling your own agent logic from scratch? Every single time.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="6362"&gt;7. Weaviate: semantic search for your private data&lt;/h3&gt;
&lt;p id="eecb"&gt;Weaviate is an open-source vector database built for AI-powered search. When you need your app to find things by &lt;em&gt;meaning&lt;/em&gt; rather than exact keyword match, Weaviate is what you reach for.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="1b60"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Hybrid search combines semantic and keyword search in one query. Stores text, images, and embeddings natively. Scales for large datasets. Docker-first for local dev, cloud-ready for production. Works seamlessly with LlamaIndex and LangChain.&lt;/em&gt;&lt;/p&gt;

&lt;p id="408a"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://weaviate.io/developers/weaviate" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;weaviate.io/developers/weaviate&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="2456"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="eb8e"&gt;pip install -U weaviate-client&lt;/span&gt;&lt;/pre&gt;
&lt;p id="dc20"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="5471"&gt;&lt;span&gt;import&lt;/span&gt; weaviate&lt;br&gt;&lt;br&gt;&lt;span&gt;# Connect to local Weaviate instance&lt;/span&gt;&lt;br&gt;client = weaviate.connect_to_local()&lt;br&gt;&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(client.is_ready())  &lt;span&gt;# True&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# Create a collection&lt;/span&gt;&lt;br&gt;questions = client.collections.get(&lt;span&gt;"Question"&lt;/span&gt;)&lt;br&gt;&lt;br&gt;&lt;span&gt;# Semantic search&lt;/span&gt;&lt;br&gt;response = questions.query.near_text(&lt;br&gt;    query=&lt;span&gt;"python data tools"&lt;/span&gt;,&lt;br&gt;    limit=&lt;span&gt;3&lt;/span&gt;&lt;br&gt;)&lt;br&gt;&lt;br&gt;&lt;span&gt;for&lt;/span&gt; obj &lt;span&gt;in&lt;/span&gt; response.objects:&lt;br&gt;    &lt;span&gt;print&lt;/span&gt;(obj.properties)&lt;br&gt;client.close()&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9428"&gt;&lt;strong&gt;Run Weaviate locally with one Docker command:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="9c79"&gt;docker run -p 8080:8080 -p 50051:50051 &amp;lt;br&amp;gt;  cr.weaviate.io/semitechnologies/weaviate:1.29.0&lt;/span&gt;&lt;/pre&gt;
&lt;p id="3c2d"&gt;The moment you stop searching by keywords and start searching by &lt;em&gt;meaning&lt;/em&gt;, you realize how much relevant data your old search was just silently missing.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="365" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AMPZQuJVXNs1YJLx71zc21w.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="2b48"&gt;8. MarkItDown: the translator between your files and your AI&lt;/h3&gt;
&lt;p id="e004"&gt;MarkItDown is a Microsoft tool that converts PDFs, Word docs, Excel sheets, PowerPoint files, and more into clean Markdown ready to feed directly into an LLM. It hit 86k GitHub stars faster than most apps hit 100 users.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="fb32"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Converts virtually any document format to Markdown in one call. Preserves structure headings, tables, lists. Designed specifically for LLM input pipelines. Zero config, dead simple API.&lt;/em&gt;&lt;/p&gt;

&lt;p id="b8a6"&gt;&lt;strong&gt;&lt;em&gt;Docs / Repo:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://github.com/microsoft/markitdown" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;github.com/microsoft/markitdown&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="13b7"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="bd72"&gt;pip install markitdown[all]&lt;/span&gt;&lt;/pre&gt;
&lt;p id="bbd0"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="be52"&gt;&lt;span&gt;from&lt;/span&gt; markitdown &lt;span&gt;import&lt;/span&gt; MarkItDown&lt;br&gt;md = MarkItDown()&lt;br&gt;&lt;br&gt;&lt;span&gt;# Convert a PDF&lt;/span&gt;&lt;br&gt;result = md.convert(&lt;span&gt;"report.pdf"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(result.text_content)&lt;br&gt;&lt;br&gt;&lt;span&gt;# Convert a Word doc&lt;/span&gt;&lt;br&gt;result = md.convert(&lt;span&gt;"proposal.docx"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(result.text_content)&lt;br&gt;&lt;br&gt;&lt;span&gt;# Convert a PowerPoint&lt;/span&gt;&lt;br&gt;result = md.convert(&lt;span&gt;"deck.pptx"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(result.text_content)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="90ca"&gt;86k stars isn’t hype. That’s developers recognizing a solved problem they’d been working around for years copying text out of PDFs by hand, fumbling with &lt;code&gt;python-docx&lt;/code&gt; just to extract paragraphs. MarkItDown killed that workflow entirely.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="f128"&gt;Web &amp;amp; APIs&lt;/h2&gt;
&lt;p id="be1f"&gt;FastAPI didn’t just win the framework wars it changed what Python devs expect from a web framework. Everything in this section is a response to that shift.&lt;/p&gt;
&lt;h3 id="f2bc"&gt;9. FastAPI: still the king, still earning it&lt;/h3&gt;
&lt;p id="46b3"&gt;FastAPI is the modern standard for building Python APIs. Async-first, type-hint driven, auto-documented. It came out swinging in 2018 and hasn’t stopped. In 2026 it’s not a trend anymore it’s the default.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="0ca4"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Automatic Swagger UI and ReDoc docs generated from your code. Built on Pydantic v2 for validation. Native async support with zero boilerplate. One of the fastest Python web frameworks available. Massive ecosystem of plugins and integrations.&lt;/em&gt;&lt;/p&gt;

&lt;p id="509a"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://fastapi.tiangolo.com" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;fastapi.tiangolo.com&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="0389"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="8f56"&gt;pip install fastapi uvicorn&lt;/span&gt;&lt;/pre&gt;
&lt;p id="84c5"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f7f7"&gt;&lt;span&gt;from&lt;/span&gt; fastapi &lt;span&gt;import&lt;/span&gt; FastAPI&lt;br&gt;&lt;span&gt;from&lt;/span&gt; pydantic &lt;span&gt;import&lt;/span&gt; BaseModel&lt;br&gt;&lt;br&gt;app = FastAPI()&lt;br&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;Item&lt;/span&gt;(&lt;span&gt;BaseModel&lt;/span&gt;):&lt;br&gt;    name: &lt;span&gt;str&lt;/span&gt;&lt;br&gt;    price: &lt;span&gt;float&lt;/span&gt;&lt;br&gt;    in_stock: &lt;span&gt;bool&lt;/span&gt; = &lt;span&gt;True&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;@app.get(&lt;span&gt;&lt;span&gt;"/"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;root&lt;/span&gt;():&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; {&lt;span&gt;"message"&lt;/span&gt;: &lt;span&gt;"Hello, FastAPI"&lt;/span&gt;}&lt;br&gt;&lt;br&gt;&lt;span&gt;@app.post(&lt;span&gt;&lt;span&gt;"/items/"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;create_item&lt;/span&gt;(&lt;span&gt;item: Item&lt;/span&gt;):&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; {&lt;span&gt;"item_name"&lt;/span&gt;: item.name, &lt;span&gt;"price"&lt;/span&gt;: item.price}&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9343"&gt;&lt;strong&gt;Run it:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f66a"&gt;uvicorn main:app --reload&lt;/span&gt;&lt;/pre&gt;
&lt;p id="2f17"&gt;Visit &lt;code&gt;&lt;a href="http://127.0.0.1:8000/docs" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/docs&lt;/a&gt;&lt;/code&gt; and your entire API is already documented, interactive, and testable. No extra work. That's still one of the best feelings in Python development.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="306" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AdhykzBZq2hPMevUVijyozA.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="db97"&gt;10. Robyn: FastAPI’s faster cousin who just got back from C++ camp&lt;/h3&gt;
&lt;p id="bf7d"&gt;Robyn is a high-performance Python web framework built on Rust internals with true multi-core support. If FastAPI is your reliable daily driver, Robyn is what you reach for when the traffic numbers stop being comfortable.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="6c21"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Benchmarks show 5x faster throughput than FastAPI on high-concurrency workloads. True multi-threading via Rust runtime. Async and sync route support. Familiar decorator syntax low learning curve if you already know Flask or FastAPI.&lt;/em&gt;&lt;/p&gt;

&lt;p id="d660"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://robyn.tech/documentation/en" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;robyn.tech/documentation&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="867c"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="39e3"&gt;pip install robyn&lt;/span&gt;&lt;/pre&gt;
&lt;p id="ff93"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f2bd"&gt;&lt;span&gt;from&lt;/span&gt; robyn &lt;span&gt;import&lt;/span&gt; Robyn, Request&lt;br&gt;&lt;br&gt;app = Robyn(&lt;strong&gt;file&lt;/strong&gt;)&lt;br&gt;&lt;br&gt;&lt;span&gt;@app.get(&lt;span&gt;&lt;span&gt;"/"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;index&lt;/span&gt;(&lt;span&gt;request: Request&lt;/span&gt;):&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; &lt;span&gt;"Hello from Robyn"&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;@app.get(&lt;span&gt;&lt;span&gt;"/users/:id"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;get_user&lt;/span&gt;(&lt;span&gt;request: Request&lt;/span&gt;):&lt;br&gt;    user_id = request.path_params.get(&lt;span&gt;"id"&lt;/span&gt;)&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; {&lt;span&gt;"user_id"&lt;/span&gt;: user_id}&lt;br&gt;&lt;br&gt;app.start(host=&lt;span&gt;"0.0.0.0"&lt;/span&gt;, port=&lt;span&gt;8080&lt;/span&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="438d"&gt;Most Python apps will never need Robyn over FastAPI. But the ones that do will feel the difference immediately and you’ll be glad you knew it existed before your architecture review.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="c85e"&gt;11. Litestar: FastAPI for engineers who like rules&lt;/h3&gt;
&lt;p id="f876"&gt;Litestar is an async Python web framework that shares FastAPI’s DNA but takes a stricter, more opinionated approach. Better dependency injection, cleaner separation of concerns, and a codebase architecture that scales better when your team grows past three people.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="3ecd"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Strict type enforcement throughout. Superior dependency injection system compared to FastAPI. Built-in OpenAPI, DTOs, and response caching. Better suited for large codebases and teams that care about architecture. Async-first without exceptions.&lt;/em&gt;&lt;/p&gt;

&lt;p id="6b75"&gt;&lt;strong&gt;&lt;em&gt;Docs: &lt;/em&gt;&lt;/strong&gt;&lt;a href="https://docs.litestar.dev" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;docs.litestar.dev&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="3d6c"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f953"&gt;pip install litestar[full]&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f9a2"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="2be8"&gt;&lt;span&gt;from&lt;/span&gt; litestar &lt;span&gt;import&lt;/span&gt; Litestar, get, post&lt;br&gt;&lt;span&gt;from&lt;/span&gt; litestar.dto &lt;span&gt;import&lt;/span&gt; DataclassDTO&lt;br&gt;&lt;span&gt;from&lt;/span&gt; dataclasses &lt;span&gt;import&lt;/span&gt; dataclass&lt;br&gt;&lt;br&gt;&lt;span&gt;@dataclass&lt;/span&gt;&lt;br&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;User&lt;/span&gt;:&lt;br&gt;    name: &lt;span&gt;str&lt;/span&gt;&lt;br&gt;    age: &lt;span&gt;int&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;&lt;a class="mentioned-user" href="https://dev.to/get"&gt;@get&lt;/a&gt;(&lt;span&gt;&lt;span&gt;"/users"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;list_users&lt;/span&gt;() -&amp;gt; &lt;span&gt;list&lt;/span&gt;[User]:&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; [User(name=&lt;span&gt;"Alice"&lt;/span&gt;, age=&lt;span&gt;30&lt;/span&gt;), User(name=&lt;span&gt;"Bob"&lt;/span&gt;, age=&lt;span&gt;25&lt;/span&gt;)]&lt;br&gt;&lt;br&gt;&lt;span&gt;&lt;a class="mentioned-user" href="https://dev.to/post"&gt;@post&lt;/a&gt;(&lt;span&gt;&lt;span&gt;"/users"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;create_user&lt;/span&gt;(&lt;span&gt;data: User&lt;/span&gt;) -&amp;gt; User:&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; data&lt;br&gt;&lt;br&gt;app = Litestar(route_handlers=[list_users, create_user])&lt;/span&gt;&lt;/pre&gt;
&lt;p id="a72b"&gt;The FastAPI vs Litestar debate is basically “do you want flexibility or guardrails?” Neither answer is wrong. It depends entirely on whether you trust your team or your team’s future interns more.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="3980"&gt;12. HTTPX: requests grew up and went async&lt;/h3&gt;
&lt;p id="09f4"&gt;HTTPX is a modern HTTP client for Python that supports both synchronous and asynchronous requests. It’s what &lt;code&gt;requests&lt;/code&gt; would look like if it were built today with async support, HTTP/2, and a cleaner API baked in from the start.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="efb3"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Drop-in replacement for &lt;/em&gt;&lt;code&gt;&lt;em&gt;requests&lt;/em&gt;&lt;/code&gt;&lt;em&gt; with async support. HTTP/2 support out of the box. Built-in timeout and retry configuration. Works perfectly inside FastAPI, LangChain, and any async codebase. Actively maintained, unlike some older alternatives.&lt;/em&gt;&lt;/p&gt;

&lt;p id="982b"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://www.python-httpx.org" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;python-httpx.org&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="1677"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="9ff4"&gt;pip install httpx&lt;/span&gt;&lt;/pre&gt;
&lt;p id="088a"&gt;&lt;strong&gt;Example sync:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="2c19"&gt;&lt;span&gt;import&lt;/span&gt; httpx&lt;br&gt;&lt;br&gt;response = httpx.get(&lt;span&gt;"&lt;a href="https://api.github.com/repos/encode/httpx" rel="noopener noreferrer"&gt;https://api.github.com/repos/encode/httpx&lt;/a&gt;"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(response.json()[&lt;span&gt;"stargazers_count"&lt;/span&gt;])&lt;/span&gt;&lt;/pre&gt;
&lt;p id="df2c"&gt;&lt;strong&gt;Example async:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="721e"&gt;&lt;span&gt;import&lt;/span&gt; httpx&lt;br&gt;&lt;span&gt;import&lt;/span&gt; asyncio&lt;br&gt;&lt;br&gt;&lt;span&gt;async&lt;/span&gt; &lt;span&gt;def&lt;/span&gt; &lt;span&gt;fetch_data&lt;/span&gt;():&lt;br&gt;    &lt;span&gt;async&lt;/span&gt; &lt;span&gt;with&lt;/span&gt; httpx.AsyncClient() &lt;span&gt;as&lt;/span&gt; client:&lt;br&gt;        response = &lt;span&gt;await&lt;/span&gt; client.get(&lt;span&gt;"&lt;a href="https://jsonplaceholder.typicode.com/posts/1" rel="noopener noreferrer"&gt;https://jsonplaceholder.typicode.com/posts/1&lt;/a&gt;"&lt;/span&gt;)&lt;br&gt;        &lt;span&gt;return&lt;/span&gt; response.json()&lt;br&gt;&lt;br&gt;&lt;span&gt;print&lt;/span&gt;(asyncio.run(fetch_data()))&lt;/span&gt;&lt;/pre&gt;
&lt;p id="d4dc"&gt;I switched from &lt;code&gt;requests&lt;/code&gt; to &lt;code&gt;httpx&lt;/code&gt; because I needed async. I stayed because &lt;code&gt;httpx&lt;/code&gt; has sane defaults, proper timeout handling, and never once made me feel like I was fighting the library to do something reasonable.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="82e3"&gt;Dev tooling &amp;amp; DX&lt;/h2&gt;
&lt;p id="8f6c"&gt;Nobody talks about this category enough. The libraries here don’t ship features they ship time. And in 2026, developer experience is finally being treated like the competitive advantage it always was.&lt;/p&gt;
&lt;h3 id="2cc8"&gt;13. Ruff: Flake8, Black, and isort walked into a bar and never came back&lt;/h3&gt;
&lt;p id="2171"&gt;Ruff is a Python linter and formatter written in Rust. It replaces Flake8, Black, isort, pyupgrade, and a handful of other tools you probably have duct-taped together in your CI pipeline right now and it does all of it 20x faster than any of them individually.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="d0e8"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; 20x faster than Flake8. Replaces multiple tools in a single binary. Auto-fixes most issues with &lt;/em&gt;&lt;code&gt;&lt;em&gt;--fix&lt;/em&gt;&lt;/code&gt;&lt;em&gt;. Works as both linter and formatter. Drop-in compatible with existing Flake8 and Black configs. Used by major open-source projects including FastAPI, Pandas, and LangChain.&lt;/em&gt;&lt;/p&gt;

&lt;p id="322e"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://docs.astral.sh/ruff/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;docs.astral.sh/ruff&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="f2c6"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="649f"&gt;pip install ruff&lt;/span&gt;&lt;/pre&gt;
&lt;p id="a436"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="9250"&gt;&lt;span&gt;# bad_code.py&lt;/span&gt;&lt;br&gt;&lt;span&gt;import&lt;/span&gt; os&lt;br&gt;&lt;span&gt;import&lt;/span&gt; sys&lt;br&gt;&lt;span&gt;import&lt;/span&gt; json  &lt;span&gt;# unused&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;calculate&lt;/span&gt;(&lt;span&gt;x,y&lt;/span&gt;):&lt;br&gt;    result=x+y&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; result&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8c48"&gt;&lt;strong&gt;Run the linter:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="cd02"&gt;ruff check bad_code.py&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span id="cd2c"&gt;bad_code.py:3:8: F401 [&lt;em&gt;] &lt;code&gt;json&lt;/code&gt; imported but unused&lt;br&gt;bad_code.py:5:17: E231 Missing whitespace after ','&lt;br&gt;Found 2 errors.&lt;br&gt;[&lt;/em&gt;] 2 fixable with the &lt;code&gt;--fix&lt;/code&gt; option.&lt;/span&gt;&lt;/pre&gt;
&lt;p id="47bf"&gt;&lt;strong&gt;Auto-fix everything:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="7c59"&gt;ruff check --fix bad_code.py&lt;br&gt;ruff format bad_code.py&lt;/span&gt;&lt;/pre&gt;
&lt;p id="95f2"&gt;Our CI pipeline dropped from four minutes to under a minute just from switching to Ruff. Nobody approved that change formally. Nobody complained either. That’s the best kind of improvement the kind nobody notices because everything just works faster.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2APft25vXxtUV1MtyfmAvlAw.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="05e0"&gt;14. UV: pip if pip actually respected your time&lt;/h3&gt;
&lt;p id="8a87"&gt;UV is an ultra-fast Python package manager and project tool written in Rust by the same team that built Ruff. It replaces pip, venv, pip-tools, and virtualenv in a single binary that installs packages 10–100x faster than pip.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="442b"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; 10–100x faster than pip. Manages virtual environments, dependencies, and Python versions in one tool. Compatible with existing &lt;/em&gt;&lt;code&gt;&lt;em&gt;pyproject.toml&lt;/em&gt;&lt;/code&gt;&lt;em&gt; and &lt;/em&gt;&lt;code&gt;&lt;em&gt;requirements.txt&lt;/em&gt;&lt;/code&gt;&lt;em&gt; workflows. Built by Astral the same team behind Ruff, so the ecosystem integration is tight.&lt;/em&gt;&lt;/p&gt;

&lt;p id="6ec4"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://docs.astral.sh/uv/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;docs.astral.sh/uv&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="039c"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="b6d0"&gt;&lt;span&gt;# macOS / Linux&lt;/span&gt;&lt;br&gt;curl -LsSf &lt;a href="https://astral.sh/uv/install.sh" rel="noopener noreferrer"&gt;https://astral.sh/uv/install.sh&lt;/a&gt; | sh&lt;br&gt;&lt;br&gt;&lt;span&gt;# Windows&lt;/span&gt;&lt;br&gt;powershell -c &lt;span&gt;"irm &lt;a href="https://astral.sh/uv/install.ps1" rel="noopener noreferrer"&gt;https://astral.sh/uv/install.ps1&lt;/a&gt; | iex"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="e892"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="3817"&gt;&lt;span&gt;# Create a new project&lt;/span&gt;&lt;br&gt;uv init my-project&lt;br&gt;&lt;span&gt;cd&lt;/span&gt; my-project&lt;br&gt;&lt;br&gt;&lt;span&gt;# Add dependencies&lt;/span&gt;&lt;br&gt;uv add fastapi uvicorn polars&lt;br&gt;&lt;br&gt;&lt;span&gt;# Run your app&lt;/span&gt;&lt;br&gt;uv run python main.py&lt;br&gt;&lt;br&gt;&lt;span&gt;# Sync dependencies from lockfile&lt;/span&gt;&lt;br&gt;uv &lt;span&gt;sync&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f52d"&gt;&lt;strong&gt;Replacing pip entirely:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="b593"&gt;&lt;span&gt;# Instead of: pip install requests&lt;/span&gt;&lt;br&gt;uv pip install requests&lt;br&gt;&lt;br&gt;&lt;span&gt;# Instead of: python -m venv .venv&lt;/span&gt;&lt;br&gt;uv venv&lt;br&gt;&lt;br&gt;&lt;span&gt;# Instead of: pip freeze &amp;gt; requirements.txt&lt;/span&gt;&lt;br&gt;uv pip freeze &amp;gt; requirements.txt&lt;/span&gt;&lt;/pre&gt;
&lt;p id="54ce"&gt;The first time you run &lt;code&gt;uv add&lt;/code&gt; on a fresh project and watch 15 packages install in two seconds, you'll feel genuinely annoyed that pip existed for this long without being this fast.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="95d0"&gt;15. TY: mypy went to therapy and came back with better boundaries&lt;/h3&gt;
&lt;p id="2883"&gt;TY is a brand new Rust-powered Python type checker and language server built by Astral. It’s the third piece of the Astral toolchain — after Ruff and uv and it’s designed to make type checking fast enough that you actually leave it on.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="680f"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Extremely fast incremental type checking — only rechecks what changed. Provides real-time editor feedback as a language server. Uses Salsa for function-level analysis so modifying one function doesn’t recheck your entire codebase. Built by the same team as Ruff and uv deep ecosystem integration incoming.&lt;/em&gt;&lt;/p&gt;

&lt;p id="3b13"&gt;&lt;strong&gt;&lt;em&gt;Docs / Repo:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://github.com/astral-sh/ty" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;github.com/astral-sh/ty&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="53d6"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="5dcb"&gt;pip install ty&lt;br&gt;&lt;span&gt;# or via uv&lt;/span&gt;&lt;br&gt;uvx ty check&lt;/span&gt;&lt;/pre&gt;
&lt;p id="fb3d"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="33e3"&gt;&lt;span&gt;# app.py&lt;/span&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;greet&lt;/span&gt;(&lt;span&gt;name: &lt;span&gt;str&lt;/span&gt;&lt;/span&gt;) -&amp;gt; &lt;span&gt;str&lt;/span&gt;:&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; &lt;span&gt;"Hello, "&lt;/span&gt; + name&lt;br&gt;&lt;br&gt;greet(&lt;span&gt;123&lt;/span&gt;)  &lt;span&gt;# passing int instead of str&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="91ab"&gt;&lt;strong&gt;Run type check:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="0f9e"&gt;ty check app.py&lt;br&gt;&lt;br&gt;error[invalid-argument-type]: Argument of &lt;span&gt;type&lt;/span&gt; &lt;code&gt;int&lt;/code&gt; cannot be &lt;br&gt;assigned to parameter &lt;code&gt;name&lt;/code&gt; of &lt;span&gt;type&lt;/span&gt; &lt;code&gt;str&lt;/code&gt;&lt;br&gt;  --&amp;gt; app.py:5:7&lt;/span&gt;&lt;/pre&gt;
&lt;p id="ef28"&gt;mypy has been the standard for years, but on large codebases it gets slow enough that developers start skipping it locally and only running it in CI. ty is fast enough that you forget it’s running which means you actually catch type errors while you’re writing the code, not twenty minutes later in a failed pipeline.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="293d"&gt;16. Prefect: Airflow for people who value their weekends&lt;/h3&gt;
&lt;p id="2776"&gt;Prefect is a modern Python-native workflow orchestration platform. If you’ve ever wrestled with Airflow’s XML configs, its scheduler quirks, or its infamous DAG serialization errors at midnight Prefect is what you switch to when you decide life is too short.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="f1b0"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Pure Python no XML, no YAML, no DSL to learn. Built-in retries, caching, logging, and observability. Develop locally, deploy anywhere with zero code changes. Modern UI for monitoring and debugging pipeline runs. Active community and solid cloud offering.&lt;/em&gt;&lt;/p&gt;

&lt;p id="9f9a"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://docs.prefect.io" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;docs.prefect.io&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="c2c1"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="a2bb"&gt;pip install prefect&lt;/span&gt;&lt;/pre&gt;
&lt;p id="e2c5"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="83bd"&gt;&lt;span&gt;from&lt;/span&gt; prefect &lt;span&gt;import&lt;/span&gt; flow, task&lt;br&gt;&lt;span&gt;import&lt;/span&gt; httpx&lt;br&gt;&lt;br&gt;&lt;span&gt;@task(&lt;span&gt;retries=&lt;span&gt;3&lt;/span&gt;, retry_delay_seconds=&lt;span&gt;10&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;fetch_data&lt;/span&gt;(&lt;span&gt;url: &lt;span&gt;str&lt;/span&gt;&lt;/span&gt;) -&amp;gt; &lt;span&gt;dict&lt;/span&gt;:&lt;br&gt;    response = httpx.get(url)&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; response.json()&lt;br&gt;&lt;br&gt;&lt;span&gt;@task&lt;/span&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;process_data&lt;/span&gt;(&lt;span&gt;data: &lt;span&gt;dict&lt;/span&gt;&lt;/span&gt;) -&amp;gt; &lt;span&gt;str&lt;/span&gt;:&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; &lt;span&gt;f"Processed: &lt;span&gt;{data.get(&lt;span&gt;'title'&lt;/span&gt;, &lt;span&gt;'No title'&lt;/span&gt;)}&lt;/span&gt;"&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;&lt;a class="mentioned-user" href="https://dev.to/flow"&gt;@flow&lt;/a&gt;(&lt;span&gt;name=&lt;span&gt;"data-pipeline"&lt;/span&gt;&lt;/span&gt;)&lt;/span&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;main_pipeline&lt;/span&gt;(&lt;span&gt;url: &lt;span&gt;str&lt;/span&gt;&lt;/span&gt;):&lt;br&gt;    raw = fetch_data(url)&lt;br&gt;    result = process_data(raw)&lt;br&gt;    &lt;span&gt;print&lt;/span&gt;(result)&lt;br&gt;&lt;br&gt;&lt;span&gt;if&lt;/span&gt; &lt;strong&gt;name&lt;/strong&gt; == &lt;span&gt;"&lt;strong&gt;main&lt;/strong&gt;"&lt;/span&gt;:&lt;br&gt;    main_pipeline(&lt;span&gt;"&lt;a href="https://jsonplaceholder.typicode.com/posts/1" rel="noopener noreferrer"&gt;https://jsonplaceholder.typicode.com/posts/1&lt;/a&gt;"&lt;/span&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8149"&gt;That &lt;code&gt;retries=3&lt;/code&gt; decorator on a task is doing what would take thirty lines of boilerplate in a hand-rolled pipeline. The fact that it also shows up in a live dashboard with full logs and run history is almost unfair.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="f5c0"&gt;UI &amp;amp; visualization&lt;/h2&gt;
&lt;p id="131b"&gt;Python has no business being this good at UI. And yet here we are.&lt;/p&gt;
&lt;h3 id="24d8"&gt;17. Rich: &lt;code&gt;print()&lt;/code&gt; for developers with standards&lt;/h3&gt;
&lt;p id="8ea3"&gt;Rich is a Python library for beautiful, readable terminal output. Tables, syntax-highlighted tracebacks, progress bars, markdown rendering, live dashboards all in your terminal, all in pure Python. Once you add Rich to a project, plain &lt;code&gt;print()&lt;/code&gt; statements start feeling disrespectful.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="a179"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Drop-in replacement for print with zero learning curve. Syntax-highlighted tracebacks that actually show you what went wrong. Built-in progress bars, spinners, tables, and panels. Works in any terminal. Used by FastAPI, Typer, and half the modern Python CLI ecosystem.&lt;/em&gt;&lt;/p&gt;

&lt;p id="3d0d"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://rich.readthedocs.io" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;rich.readthedocs.io&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="c641"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="76ed"&gt;pip install rich&lt;/span&gt;&lt;/pre&gt;
&lt;p id="7efe"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="3f61"&gt;&lt;span&gt;from&lt;/span&gt; rich.console &lt;span&gt;import&lt;/span&gt; Console&lt;br&gt;&lt;span&gt;from&lt;/span&gt; rich.table &lt;span&gt;import&lt;/span&gt; Table&lt;br&gt;&lt;span&gt;from&lt;/span&gt; rich.progress &lt;span&gt;import&lt;/span&gt; track&lt;br&gt;&lt;span&gt;import&lt;/span&gt; time&lt;br&gt;&lt;br&gt;console = Console()&lt;br&gt;&lt;br&gt;&lt;span&gt;# Beautiful tables&lt;/span&gt;&lt;br&gt;table = Table(title=&lt;span&gt;"Python Libraries 2026"&lt;/span&gt;)&lt;br&gt;table.add_column(&lt;span&gt;"Library"&lt;/span&gt;, style=&lt;span&gt;"cyan"&lt;/span&gt;)&lt;br&gt;table.add_column(&lt;span&gt;"Category"&lt;/span&gt;, style=&lt;span&gt;"magenta"&lt;/span&gt;)&lt;br&gt;table.add_column(&lt;span&gt;"Language"&lt;/span&gt;, style=&lt;span&gt;"green"&lt;/span&gt;)&lt;br&gt;&lt;br&gt;table.add_row(&lt;span&gt;"Polars"&lt;/span&gt;, &lt;span&gt;"Data"&lt;/span&gt;, &lt;span&gt;"Rust"&lt;/span&gt;)&lt;br&gt;table.add_row(&lt;span&gt;"Ruff"&lt;/span&gt;, &lt;span&gt;"Tooling"&lt;/span&gt;, &lt;span&gt;"Rust"&lt;/span&gt;)&lt;br&gt;table.add_row(&lt;span&gt;"FastAPI"&lt;/span&gt;, &lt;span&gt;"Web"&lt;/span&gt;, &lt;span&gt;"Python"&lt;/span&gt;)&lt;br&gt;&lt;br&gt;console.&lt;span&gt;print&lt;/span&gt;(table)&lt;br&gt;&lt;br&gt;&lt;span&gt;# Progress bar&lt;/span&gt;&lt;br&gt;&lt;span&gt;for&lt;/span&gt; step &lt;span&gt;in&lt;/span&gt; track(&lt;span&gt;range&lt;/span&gt;(&lt;span&gt;10&lt;/span&gt;), description=&lt;span&gt;"Processing..."&lt;/span&gt;):&lt;br&gt;    time.sleep(&lt;span&gt;0.1&lt;/span&gt;)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="fd10"&gt;I added Rich to a data pipeline at work as a “quick weekend improvement.” On Monday, three people separately asked if we’d hired a frontend developer. We hadn’t. That’s the Rich effect.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AD5z-xYitZrbqRwgLUlFw5w.jpeg"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="b02c"&gt;18. Textual: terminal apps your stakeholders will think are real products&lt;/h3&gt;
&lt;p id="6fd7"&gt;Textual is a framework for building full TUI (Terminal User Interface) applications in Python. Built by the same team behind Rich, it brings CSS-style layouts, reactive components, and event-driven architecture to your terminal. The result looks like a proper app not a shell script.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="73c4"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; CSS-inspired layout system actual responsive design in a terminal. Reactive components with state management built in. Rich integration for beautiful output by default. Works over SSH so you can deploy terminal apps to servers. No frontend experience needed.&lt;/em&gt;&lt;/p&gt;

&lt;p id="df8c"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://textual.textualize.io/tutorial/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;textual.textualize.io&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="5e44"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="df38"&gt;pip install textual&lt;/span&gt;&lt;/pre&gt;
&lt;p id="639e"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="ad91"&gt;&lt;span&gt;from&lt;/span&gt; textual.app &lt;span&gt;import&lt;/span&gt; App, ComposeResult&lt;br&gt;&lt;span&gt;from&lt;/span&gt; textual.widgets &lt;span&gt;import&lt;/span&gt; Header, Footer, Button, Label&lt;br&gt;&lt;span&gt;from&lt;/span&gt; textual.containers &lt;span&gt;import&lt;/span&gt; Center&lt;br&gt;&lt;br&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;DevDashboard&lt;/span&gt;(&lt;span&gt;App&lt;/span&gt;):&lt;br&gt;    CSS = &lt;span&gt;"""&lt;br&gt;    Center { align: center middle; }&lt;br&gt;    Button { margin: 1; width: 20; }&lt;br&gt;    """&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span&gt;def&lt;/span&gt; &lt;span&gt;compose&lt;/span&gt;(&lt;span&gt;self&lt;/span&gt;) -&amp;gt; ComposeResult:&lt;br&gt;        &lt;span&gt;yield&lt;/span&gt; Header()&lt;br&gt;        &lt;span&gt;yield&lt;/span&gt; Center(&lt;br&gt;            Label(&lt;span&gt;"🚀 Deploy to production?"&lt;/span&gt;, &lt;span&gt;id&lt;/span&gt;=&lt;span&gt;"title"&lt;/span&gt;),&lt;br&gt;            Button(&lt;span&gt;"Ship it"&lt;/span&gt;, &lt;span&gt;id&lt;/span&gt;=&lt;span&gt;"ship"&lt;/span&gt;, variant=&lt;span&gt;"success"&lt;/span&gt;),&lt;br&gt;            Button(&lt;span&gt;"Not today"&lt;/span&gt;, &lt;span&gt;id&lt;/span&gt;=&lt;span&gt;"abort"&lt;/span&gt;, variant=&lt;span&gt;"error"&lt;/span&gt;),&lt;br&gt;        )&lt;br&gt;        &lt;span&gt;yield&lt;/span&gt; Footer()&lt;br&gt;&lt;br&gt;    &lt;span&gt;def&lt;/span&gt; &lt;span&gt;on_button_pressed&lt;/span&gt;(&lt;span&gt;self, event: Button.Pressed&lt;/span&gt;) -&amp;gt; &lt;span&gt;None&lt;/span&gt;:&lt;br&gt;        &lt;span&gt;if&lt;/span&gt; event.button.&lt;span&gt;id&lt;/span&gt; == &lt;span&gt;"ship"&lt;/span&gt;:&lt;br&gt;            self.exit(&lt;span&gt;"Deploying..."&lt;/span&gt;)&lt;br&gt;        &lt;span&gt;else&lt;/span&gt;:&lt;br&gt;            self.exit(&lt;span&gt;"Aborted. Wise choice."&lt;/span&gt;)&lt;br&gt;&lt;br&gt;&lt;span&gt;if&lt;/span&gt; &lt;strong&gt;name&lt;/strong&gt; == &lt;span&gt;"&lt;strong&gt;main&lt;/strong&gt;"&lt;/span&gt;:&lt;br&gt;    app = DevDashboard()&lt;br&gt;    &lt;span&gt;print&lt;/span&gt;(app.run())&lt;/span&gt;&lt;/pre&gt;
&lt;p id="2d45"&gt;The fact that you can build something that looks like a real product dashboard keyboard navigation, mouse support, live updating data without touching HTML or JavaScript once, is genuinely one of the more underrated Python superpowers in 2026.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="017a"&gt;19. Flet: Flutter for Python developers who never wanted to learn Dart&lt;/h3&gt;
&lt;p id="48ee"&gt;Flet lets you build web, desktop, and mobile apps in pure Python using Flutter under the hood. One codebase. Three platforms. Zero JavaScript, zero TypeScript, zero Dart. If you’ve been putting off building a frontend because you didn’t want to learn a whole new language, Flet removes that excuse entirely.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="9000"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Build for web, desktop, and mobile from a single Python file. Flutter-based so the UI looks genuinely good out of the box. Reactive state management included. Hot reload during development. No frontend knowledge required if you know Python, you can ship a UI.&lt;/em&gt;&lt;/p&gt;

&lt;p id="26cb"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://flet.dev/docs/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;flet.dev/docs&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="b651"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="52a6"&gt;pip install flet&lt;/span&gt;&lt;/pre&gt;
&lt;p id="eb74"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="ea98"&gt;&lt;span&gt;import&lt;/span&gt; flet &lt;span&gt;as&lt;/span&gt; ft&lt;br&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;main&lt;/span&gt;(&lt;span&gt;page: ft.Page&lt;/span&gt;):&lt;br&gt;    page.title = &lt;span&gt;"Dev Tools Dashboard"&lt;/span&gt;&lt;br&gt;    page.theme_mode = ft.ThemeMode.DARK&lt;br&gt;&lt;br&gt;    status = ft.Text(&lt;span&gt;"Status: idle"&lt;/span&gt;, size=&lt;span&gt;16&lt;/span&gt;)&lt;br&gt;&lt;br&gt;    &lt;span&gt;def&lt;/span&gt; &lt;span&gt;run_pipeline&lt;/span&gt;(&lt;span&gt;e&lt;/span&gt;):&lt;br&gt;        status.value = &lt;span&gt;"Status: running pipeline..."&lt;/span&gt;&lt;br&gt;        page.update()&lt;br&gt;&lt;br&gt;    page.add(&lt;br&gt;        ft.Column([&lt;br&gt;            ft.Text(&lt;span&gt;"🛠️ Pipeline Runner"&lt;/span&gt;, size=&lt;span&gt;24&lt;/span&gt;, weight=&lt;span&gt;"bold"&lt;/span&gt;),&lt;br&gt;            ft.ElevatedButton(&lt;span&gt;"Run pipeline"&lt;/span&gt;, on_click=run_pipeline),&lt;br&gt;            status,&lt;br&gt;        ])&lt;br&gt;    )&lt;br&gt;&lt;br&gt;ft.app(target=main)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5ad5"&gt;&lt;strong&gt;Run as a desktop app:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="5098"&gt;python main.py&lt;/span&gt;&lt;/pre&gt;
&lt;p id="2a04"&gt;&lt;strong&gt;Run as a web app change one line:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="f620"&gt;ft.app(target=main, view=ft.AppView.WEB_BROWSER)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="13ba"&gt;That single-line switch from desktop to web is the kind of thing that makes you question every frontend project you’ve ever spent three weeks setting up from scratch.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h3 id="05d6"&gt;20. Reflex: React and Python had a baby and dropped TypeScript at the hospital&lt;/h3&gt;
&lt;p id="f0f0"&gt;Reflex is a full-stack web framework that lets you build modern reactive web applications entirely in Python. Frontend, backend, state management all Python. No React. No TypeScript. No webpack config that makes you question your career choices.&lt;/p&gt;
&lt;blockquote&gt;

&lt;p id="ccbb"&gt;&lt;strong&gt;&lt;em&gt;Why you should use it:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; Full-stack web apps in pure Python. React-like component model without leaving Python. Built-in state management no Redux, no Zustand, no context hell. SSR and SEO-friendly out of the box. Backend and frontend share the same state object. Active development with a growing component library.&lt;/em&gt;&lt;/p&gt;

&lt;p id="4b9c"&gt;&lt;strong&gt;&lt;em&gt;Docs:&lt;/em&gt;&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;&lt;a href="https://reflex.dev/docs/getting-started/introduction/" rel="noopener ugc nofollow noreferrer"&gt;&lt;em&gt;reflex.dev/docs&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;


&lt;/blockquote&gt;
&lt;p id="a985"&gt;&lt;strong&gt;Installation:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="6336"&gt;pip install reflex&lt;/span&gt;&lt;/pre&gt;
&lt;p id="7e50"&gt;&lt;strong&gt;Example:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="320b"&gt;&lt;span&gt;import&lt;/span&gt; reflex &lt;span&gt;as&lt;/span&gt; rx&lt;br&gt;&lt;br&gt;&lt;span&gt;class&lt;/span&gt; &lt;span&gt;State&lt;/span&gt;(rx.State):&lt;br&gt;    count: &lt;span&gt;int&lt;/span&gt; = &lt;span&gt;0&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span&gt;def&lt;/span&gt; &lt;span&gt;increment&lt;/span&gt;(&lt;span&gt;self&lt;/span&gt;):&lt;br&gt;        self.count += &lt;span&gt;1&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span&gt;def&lt;/span&gt; &lt;span&gt;decrement&lt;/span&gt;(&lt;span&gt;self&lt;/span&gt;):&lt;br&gt;        self.count -= &lt;span&gt;1&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;def&lt;/span&gt; &lt;span&gt;index&lt;/span&gt;():&lt;br&gt;    &lt;span&gt;return&lt;/span&gt; rx.center(&lt;br&gt;        rx.vstack(&lt;br&gt;            rx.text(&lt;span&gt;f"Count: &lt;span&gt;{State.count}&lt;/span&gt;"&lt;/span&gt;, font_size=&lt;span&gt;"2em"&lt;/span&gt;),&lt;br&gt;            rx.hstack(&lt;br&gt;                rx.button(&lt;br&gt;                    &lt;span&gt;"−"&lt;/span&gt;,&lt;br&gt;                    on_click=State.decrement,&lt;br&gt;                    color_scheme=&lt;span&gt;"red"&lt;/span&gt;&lt;br&gt;                ),&lt;br&gt;                rx.button(&lt;br&gt;                    &lt;span&gt;"+"&lt;/span&gt;,&lt;br&gt;                    on_click=State.increment,&lt;br&gt;                    color_scheme=&lt;span&gt;"green"&lt;/span&gt;&lt;br&gt;                ),&lt;br&gt;            ),&lt;br&gt;            spacing=&lt;span&gt;"4"&lt;/span&gt;,&lt;br&gt;        )&lt;br&gt;    )&lt;br&gt;&lt;br&gt;app = rx.App()&lt;br&gt;app.add_page(index)&lt;/span&gt;&lt;/pre&gt;
&lt;p id="d6d6"&gt;&lt;strong&gt;Run development server:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="ac98"&gt;reflex run&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8543"&gt;The state management model where your Python class &lt;em&gt;is&lt;/em&gt; your app state and mutations just work across frontend and backend is the part that hooks you. You write Python, the UI updates. That’s it. No serialization layer to think about. No API endpoints to wire up for every button click.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="283b"&gt;Final thoughts&lt;/h2&gt;
&lt;p id="a841"&gt;Here’s the thing nobody says out loud: Python’s biggest competitive advantage in 2026 isn’t the language itself. It’s the culture of developers who refuse to ship slow, painful, ugly tooling when they know it can be better.&lt;/p&gt;
&lt;p id="93a7"&gt;Every library in this list exists because someone got frustrated enough to fix something. Ruff exists because linting was too slow. Polars exists because Pandas wasn’t built for modern hardware. uv exists because pip never respected your time. Rich exists because terminal output was embarrassingly bad for a language used by millions of developers daily.&lt;/p&gt;
&lt;p id="8c30"&gt;That’s not a criticism of the old tools. That’s just how good ecosystems evolve iteratively, impatiently, and usually in Rust.&lt;/p&gt;
&lt;p id="2cb5"&gt;The Astral team alone the people behind Ruff, uv, and now ty have done more to modernize the Python developer experience in two years than the broader ecosystem managed in the previous ten. Keep watching them. Whatever they ship next is probably going to replace something you’re currently running in CI.&lt;/p&gt;
&lt;p id="c6e2"&gt;The honest advice: you don’t need all twenty of these today. Pick one from each category that solves a real problem you have right now. Swap Pandas for Polars on your next data task. Drop &lt;code&gt;requests&lt;/code&gt; for &lt;code&gt;httpx&lt;/code&gt; in your next async service. Add Rich to the pipeline you've been embarrassed to show your team. Small swaps, compounding returns.&lt;/p&gt;
&lt;p id="3e09"&gt;Python isn’t slowing down. It’s getting sharper, faster, and more intentional with every year. The developers who stay ahead aren’t the ones who learn everything they’re the ones who know which ten percent actually matters.&lt;/p&gt;
&lt;p id="e44d"&gt;Now you do.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="36f7"&gt;What’s the first one you’re adding to your stack? Drop it in the comments.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="71ea"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="a341"&gt;&lt;a href="https://docs.pola.rs/" rel="noopener ugc nofollow noreferrer"&gt;Polars documentation&lt;/a&gt;&lt;/li&gt;

&lt;li id="e42a"&gt;&lt;a href="https://duckdb.org/docs/stable/clients/python/overview.html" rel="noopener ugc nofollow noreferrer"&gt;DuckDB Python client docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="05af"&gt;&lt;a href="https://astral.sh" rel="noopener ugc nofollow noreferrer"&gt;Astral ecosystem &lt;strong&gt;Ruff, uv, ty&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;

&lt;li id="e0d2"&gt;&lt;a href="https://github.com/microsoft/markitdown" rel="noopener ugc nofollow noreferrer"&gt;MarkItDown GitHub repo&lt;/a&gt;&lt;/li&gt;

&lt;li id="1af5"&gt;&lt;a href="https://tryolabs.com/blog/top-python-libraries-2025" rel="noopener ugc nofollow noreferrer"&gt;Tryolabs Top Python Libraries 2025 report&lt;/a&gt;&lt;/li&gt;

&lt;li id="8efd"&gt;&lt;a href="https://www.kdnuggets.com/12-python-libraries-you-need-to-try-in-2026" rel="noopener ugc nofollow noreferrer"&gt;KDnuggets &lt;strong&gt;12 Python libraries to try in 2026&lt;/strong&gt;&lt;/a&gt;&lt;/li&gt;

&lt;li id="a9af"&gt;&lt;a href="https://docs.prefect.io" rel="noopener ugc nofollow noreferrer"&gt;Prefect documentation&lt;/a&gt;&lt;/li&gt;

&lt;li id="ac60"&gt;&lt;a href="https://reflex.dev/docs/getting-started/introduction/" rel="noopener ugc nofollow noreferrer"&gt;Reflex getting started&lt;/a&gt;&lt;/li&gt;

&lt;li id="7740"&gt;&lt;a href="https://docs.llamaindex.ai/en/stable/" rel="noopener ugc nofollow noreferrer"&gt;LlamaIndex documentation&lt;/a&gt;&lt;/li&gt;

&lt;li id="505e"&gt;&lt;a href="https://weaviate.io/developers/weaviate" rel="noopener ugc nofollow noreferrer"&gt;Weaviate documentation&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
      <category>python</category>
    </item>
    <item>
      <title>I stopped deploying manually. Claude Code and 7 tools do it now.</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Sat, 18 Apr 2026 15:01:44 +0000</pubDate>
      <link>https://forem.com/dev_tips/i-stopped-deploying-manually-claude-code-and-7-tools-do-it-now-2oic</link>
      <guid>https://forem.com/dev_tips/i-stopped-deploying-manually-claude-code-and-7-tools-do-it-now-2oic</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="c835"&gt;&lt;strong&gt;From 9 hours a week babysitting deployments to 20 minutes reviewing what the agent already did. Here’s the exact stack.&lt;/strong&gt;&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;&lt;/blockquote&gt;
&lt;p id="d49b"&gt;It was a regular evening. Movie on. Phone face-down. Then Slack buzzed.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="ce0d"&gt;&lt;em&gt;“API is down. Users can’t login.”&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="60c4"&gt;I knew the fix immediately connection pool exhausted, classic. Restart the service, bump the pool size, redeploy. Twenty minutes max. Except my deployment script decided that was a great night to die halfway through. SSH connection dropped. Restarted from scratch. Config edit directly in prod because I was panicking. Finally back up forty-five minutes later.&lt;/p&gt;
&lt;p id="d013"&gt;The movie had moved on without me. So had my will to live.&lt;/p&gt;
&lt;p id="ecc9"&gt;The next morning I pulled my deployment history for the last month. Forty-seven manual deployments. Average time per deploy: thirty-eight minutes. Total time spent: roughly thirty hours. That’s almost a full work week. Just deploying code. Manually. Like it’s 2015.&lt;/p&gt;
&lt;p id="460f"&gt;I didn’t have a DevOps problem. I had a “I never designed this workflow” problem. The deployments worked they just required me to babysit every single one like a process that couldn’t be trusted to run unsupervised. Spoiler: it couldn’t. Because nothing was automated and everything depended on me not making a typo at eleven PM.&lt;/p&gt;
&lt;p id="fe59"&gt;That month I started wiring up Claude Code with seven tools. Not all at once one integration at a time over about ten weeks. What I ended up with is a deployment pipeline that runs itself, monitors itself, and tells me what happened in Slack while I’m doing literally anything else.&lt;/p&gt;
&lt;p id="b71d"&gt;This is the exact stack, how it fits together, and the mistakes I made building it so you don’t have to repeat them.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="a0b1"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; Claude Code isn’t a chatbot you paste kubectl commands into. It’s a terminal-native agent. Give it real tool access and it becomes a DevOps co-pilot that actually operates your stack.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="e143"&gt;What Claude Code actually is (and what most devs get wrong)&lt;/h2&gt;
&lt;p id="75d9"&gt;Most people hear “Claude Code” and picture a smarter Copilot. Autocomplete with better vibes. A chatbot that knows what &lt;code&gt;kubectl&lt;/code&gt; means. That mental model will make you use it wrong.&lt;/p&gt;
&lt;p id="0078"&gt;Claude Code is a terminal-native agentic AI. It runs in your shell, has access to your filesystem, executes commands, reads outputs, reacts to errors, and chains multiple actions together without you holding its hand through every step. It doesn’t suggest it does. You give it a goal, it figures out the steps, runs them, checks the output, and adjusts.&lt;/p&gt;
&lt;p id="f433"&gt;The unlock isn’t the AI. It’s what you give the AI access to.&lt;/p&gt;
&lt;p id="810e"&gt;Most devs use it like a search engine. “Hey Claude, how do I write a GitHub Actions workflow for Node?” Cool, you got an answer. You could’ve Googled that. The actual unlock is when you stop asking it things and start giving it access to things your repo, your CI config, your cluster credentials, your alerting setup. That’s when it stops being a productivity boost and starts being an actual workflow layer.&lt;/p&gt;
&lt;p id="9e0f"&gt;&lt;strong&gt;The first time it clicked for me:&lt;/strong&gt; a GitHub Actions pipeline failed on a dependency conflict. Instead of me digging through logs, Claude Code read the failure output, identified the version mismatch, updated &lt;code&gt;package.json&lt;/code&gt;, re-ran the workflow, and posted a Slack summary. I was in another tab. Didn't touch it once. That felt genuinely strange the good kind of strange, like the first time a cron job ran and you weren't sure whether to feel proud or nervous.&lt;/p&gt;
&lt;p id="f55c"&gt;That’s the tool. What you wire it to determines what it’s capable of.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="c87d"&gt;The 7 tools that make it a real DevOps co-pilot&lt;/h2&gt;
&lt;p id="04b9"&gt;Not a “here are some tools to explore” list. These are the seven things I connected, what each one does, and what Claude Code does with access to them.&lt;/p&gt;
&lt;h3 id="8a1d"&gt;1. Docker&lt;/h3&gt;
&lt;p id="eebd"&gt;The foundation. Claude Code writes the Dockerfile, builds the image, reads errors mid-build, and fixes them in the same session. Hand it your app structure and it handles multi-stage builds, layer caching, and base image optimization without you reading docs.&lt;/p&gt;
&lt;p id="9d8a"&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="226a"&gt;&lt;span&gt;# Claude Code generated this after reading the repo structure&lt;/span&gt;&lt;br&gt;FROM node:20-alpine AS builder&lt;br&gt;WORKDIR /app&lt;br&gt;COPY package*.json ./&lt;br&gt;RUN npm ci --only=production&lt;br&gt;&lt;br&gt;FROM node:20-alpine&lt;br&gt;WORKDIR /app&lt;br&gt;COPY --from=builder /app/node_modules ./node_modules&lt;br&gt;COPY . .&lt;br&gt;EXPOSE 3000&lt;br&gt;CMD [&lt;span&gt;"node"&lt;/span&gt;, &lt;span&gt;"server.js"&lt;/span&gt;]&lt;/span&gt;&lt;/pre&gt;
&lt;p id="b79b"&gt;I asked it to optimize my existing Dockerfile for size. Went from 850MB to 180MB by switching base images and adding multi-stage builds. Took three minutes.&lt;/p&gt;
&lt;p id="2d2e"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Manual builds, manual pushes, “works on my machine” debugging sessions. &lt;strong&gt;After:&lt;/strong&gt; Claude Code builds, tags, and pushes. I review the output.&lt;/p&gt;
&lt;h3 id="6250"&gt;2. GitHub Actions&lt;/h3&gt;
&lt;p id="d493"&gt;Your CI/CD pipeline lives in YAML that nobody enjoys writing and everyone breaks at least once a sprint. Claude Code reads your existing workflows, edits them, adds jobs, and when a run fails — reads the logs, finds the problem, patches the file, and pushes the fix.&lt;/p&gt;
&lt;pre&gt;&lt;span id="9a72"&gt;&lt;span&gt;# Claude Code added this after a deploy left no rollback path&lt;/span&gt;&lt;br&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;Rollback&lt;/span&gt; &lt;span&gt;on&lt;/span&gt; &lt;span&gt;failure&lt;/span&gt;&lt;br&gt;  &lt;span&gt;if:&lt;/span&gt; &lt;span&gt;failure()&lt;/span&gt;&lt;br&gt;  &lt;span&gt;run:&lt;/span&gt; &lt;span&gt;|&lt;br&gt;    echo "Deploy failed. Rolling back..."&lt;br&gt;    kubectl rollout undo deployment/myapp&lt;br&gt;    kubectl rollout status deployment/myapp&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="129b"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Hours debugging YAML indentation and missing environment variables. &lt;strong&gt;After:&lt;/strong&gt; Claude Code generated 90% of my workflows. I just review and merge.&lt;/p&gt;
&lt;h3 id="94b7"&gt;3. Kubernetes&lt;/h3&gt;
&lt;p id="8006"&gt;Manifests are verbose, unforgiving, and somehow always hiding one wrong indent. Claude Code writes them from scratch, applies them via &lt;code&gt;kubectl&lt;/code&gt;, reads pod status, checks logs, and rolls back when things go sideways.&lt;/p&gt;
&lt;p id="6ea9"&gt;&lt;strong&gt;Hcl&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="6e11"&gt;&lt;span&gt;# Claude Code runs this sequence after a failed health check&lt;/span&gt;&lt;br&gt;kubectl rollout status deployment/myapp --&lt;span&gt;timeout&lt;/span&gt;=60s&lt;br&gt;kubectl logs deployment/myapp --&lt;span&gt;tail&lt;/span&gt;=50&lt;br&gt;kubectl rollout undo deployment/myapp&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9163"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Manual kubectl commands, SSH tunnels, VPN connections, pain. &lt;strong&gt;After:&lt;/strong&gt; Claude Code manages the manifests. I review before apply.&lt;/p&gt;
&lt;h3 id="9ce5"&gt;4. Terraform&lt;/h3&gt;
&lt;p id="77b3"&gt;Infra-as-code is powerful and also a great way to accidentally delete a database. Claude Code generates configs, runs plans, reads the diff output, and applies changes. Excellent at scaffolding new resources and catching config drift.&lt;/p&gt;
&lt;pre&gt;&lt;span id="0f50"&gt;&lt;span&gt;# Claude Code generated this after "spin up a staging RDS instance"&lt;/span&gt;&lt;br&gt;resource &lt;span&gt;"aws_db_instance"&lt;/span&gt; &lt;span&gt;"staging"&lt;/span&gt; &lt;span&gt;{&lt;/span&gt;&lt;br&gt;  identifier        &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"myapp-staging"&lt;/span&gt;&lt;br&gt;  engine            &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"postgres"&lt;/span&gt;&lt;br&gt;  engine_version    &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"15.3"&lt;/span&gt;&lt;br&gt;  instance_class    &lt;span&gt;=&lt;/span&gt; &lt;span&gt;"db.t3.micro"&lt;/span&gt;&lt;br&gt;  allocated_storage &lt;span&gt;=&lt;/span&gt; &lt;span&gt;20&lt;/span&gt;&lt;br&gt;  skip_final_snapshot &lt;span&gt;=&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;br&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="4641"&gt;Always review the plan output before apply. Claude Code will show you the diff read it. This is the one place where “looks good” is not sufficient review.&lt;/p&gt;
&lt;p id="b78c"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Manual AWS console clicking, forgetting what I configured, breaking things. &lt;strong&gt;After:&lt;/strong&gt; &lt;code&gt;terraform apply&lt;/code&gt; → infrastructure deployed. Version controlled. Reproducible.&lt;/p&gt;
&lt;h3 id="5807"&gt;5. ArgoCD&lt;/h3&gt;
&lt;p id="b882"&gt;This one changed how I think about deployments entirely. ArgoCD watches your Git repo when you push a new Kubernetes manifest, it automatically syncs it to your cluster. Your Git repo becomes the single source of truth. No manual &lt;code&gt;kubectl apply&lt;/code&gt;. No "did I deploy the right version?" confusion.&lt;/p&gt;
&lt;pre&gt;&lt;span id="a60a"&gt;&lt;span&gt;# ArgoCD application config — Claude Code generated this&lt;/span&gt;&lt;br&gt;&lt;span&gt;apiVersion:&lt;/span&gt; &lt;span&gt;argoproj.io/v1alpha1&lt;/span&gt;&lt;br&gt;&lt;span&gt;kind:&lt;/span&gt; &lt;span&gt;Application&lt;/span&gt;&lt;br&gt;&lt;span&gt;metadata:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;myapp&lt;/span&gt;&lt;br&gt;&lt;span&gt;spec:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;source:&lt;/span&gt;&lt;br&gt;    &lt;span&gt;repoURL:&lt;/span&gt; &lt;span&gt;&lt;a href="https://github.com/myorg/myapp" rel="noopener noreferrer"&gt;https://github.com/myorg/myapp&lt;/a&gt;&lt;/span&gt;&lt;br&gt;    &lt;span&gt;path:&lt;/span&gt; &lt;span&gt;k8s/&lt;/span&gt;&lt;br&gt;    &lt;span&gt;targetRevision:&lt;/span&gt; &lt;span&gt;main&lt;/span&gt;&lt;br&gt;  &lt;span&gt;destination:&lt;/span&gt;&lt;br&gt;    &lt;span&gt;server:&lt;/span&gt; &lt;span&gt;&lt;a href="https://kubernetes.default.svc" rel="noopener noreferrer"&gt;https://kubernetes.default.svc&lt;/a&gt;&lt;/span&gt;&lt;br&gt;    &lt;span&gt;namespace:&lt;/span&gt; &lt;span&gt;production&lt;/span&gt;&lt;br&gt;  &lt;span&gt;syncPolicy:&lt;/span&gt;&lt;br&gt;    &lt;span&gt;automated:&lt;/span&gt;&lt;br&gt;      &lt;span&gt;prune:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;br&gt;      &lt;span&gt;selfHeal:&lt;/span&gt; &lt;span&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f402"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Manual kubectl commands, wondering if staging and prod were in sync. &lt;strong&gt;After:&lt;/strong&gt; Push to Git → ArgoCD syncs → deployed. Claude Code generates all the manifests.&lt;/p&gt;
&lt;h3 id="50b9"&gt;6. Datadog&lt;/h3&gt;
&lt;p id="d8f0"&gt;Observability is only useful if someone’s actually reading it. Claude Code connected to Datadog reads active alerts, pulls recent metrics, correlates a spike with a recent deploy, and suggests whether to roll back or hold. It caught a memory leak mid-deploy before PagerDuty even fired.&lt;/p&gt;
&lt;pre&gt;&lt;span id="5956"&gt;&lt;span&gt;# Claude Code queried this after spotting a latency anomaly&lt;/span&gt;&lt;br&gt;curl -X GET &lt;span&gt;"&lt;a href="https://api.datadoghq.com/api/v1/events" rel="noopener noreferrer"&gt;https://api.datadoghq.com/api/v1/events&lt;/a&gt;"&lt;/span&gt; &amp;lt;br&amp;gt;  -H &lt;span&gt;"DD-API-KEY: &lt;span&gt;$DD_API_KEY&lt;/span&gt;"&lt;/span&gt; &amp;lt;br&amp;gt;  -H &lt;span&gt;"DD-APPLICATION-KEY: &lt;span&gt;$DD_APP_KEY&lt;/span&gt;"&lt;/span&gt; &amp;lt;br&amp;gt;  -d &lt;span&gt;"start=&lt;span&gt;$(date -d '30 minutes ago' +%s)&lt;/span&gt;&amp;amp;end=&lt;span&gt;$(date +%s)&lt;/span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8c23"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Found out about problems from angry users in support tickets. &lt;strong&gt;After:&lt;/strong&gt; Found out about problems before users noticed. Sometimes before I noticed.&lt;/p&gt;
&lt;h3 id="75dd"&gt;7. Slack + PagerDuty&lt;/h3&gt;
&lt;p id="6771"&gt;Incident response is 40% fixing things and 60% telling people what’s happening. Claude Code wired into Slack means when something breaks it’s already posting the incident summary, updating the right channels, and drafting the runbook while you’re still figuring out what’s on fire.&lt;/p&gt;
&lt;pre&gt;&lt;span id="1983"&gt;&lt;span&gt;# Auto-posted to #incidents during a recent outage&lt;/span&gt;&lt;br&gt;curl -X POST &lt;a href="https://slack.com/api/chat.postMessage" rel="noopener noreferrer"&gt;https://slack.com/api/chat.postMessage&lt;/a&gt; &amp;lt;br&amp;gt;  -H &lt;span&gt;"Authorization: Bearer &lt;span&gt;$SLACK_TOKEN&lt;/span&gt;"&lt;/span&gt; &amp;lt;br&amp;gt;  -d &lt;span&gt;"channel=#incidents"&lt;/span&gt; &amp;lt;br&amp;gt;  -d &lt;span&gt;"text=🚨 Deploy myapp:v2.3.1 caused 500 spike. Rolling back. ETA 3 min."&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5469"&gt;I also built a Slack bot with Claude’s help that answers questions like “what’s the current error rate?” by querying Datadog and replying inline. Took an afternoon to set up. Saved dozens of dashboard context-switches since.&lt;/p&gt;
&lt;p id="07f4"&gt;&lt;strong&gt;Before:&lt;/strong&gt; Check GitHub. Check Datadog. Check ArgoCD. Check logs. Repeat. &lt;strong&gt;After:&lt;/strong&gt; Everything surfaces in Slack. One place. One thread per incident.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AJzdvfYkDvh-_JTegA0j7dw.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="a61c"&gt;A real deployment flow, start to finish&lt;/h2&gt;
&lt;p id="789b"&gt;Let me walk you through an actual deploy. Not a sanitized demo a real one, including the part where it almost did something stupid.&lt;/p&gt;
&lt;p id="f33d"&gt;The stack: Node.js API, GitHub repo, Docker builds, Kubernetes cluster on AWS, ArgoCD for GitOps sync, Datadog for monitoring, Slack for everything else. Standard mid-size setup.&lt;/p&gt;
&lt;p id="1c43"&gt;I pushed a feature branch, opened a PR, and from that point Claude Code handled the rest.&lt;/p&gt;
&lt;h3 id="6614"&gt;&lt;strong&gt;Step 1: PR opened, CI triggered, test failure caught&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="42fd"&gt;Claude Code detected the new PR via the GitHub MCP server and checked the Actions workflow status. First run failed a null reference in the auth middleware I’d missed locally.&lt;/p&gt;
&lt;pre&gt;&lt;span id="bdde"&gt;&lt;span&gt;# Claude Code read the Actions log and identified the failure&lt;/span&gt;&lt;br&gt;gh run view 8842931 --log-failed&lt;br&gt;&lt;br&gt;&lt;span&gt;# Found the issue, patched it, pushed the fix&lt;/span&gt;&lt;br&gt;git add src/middleware/auth.js&lt;br&gt;git commit -m &lt;span&gt;"fix: null check on req.user before role validation"&lt;/span&gt;&lt;br&gt;git push origin feature/user-permissions&lt;/span&gt;&lt;/pre&gt;
&lt;p id="fe3a"&gt;No ping. No “hey can you check this.” It just fixed it and moved on.&lt;/p&gt;
&lt;h3 id="59c4"&gt;&lt;strong&gt;Step 2: Tests passed, image built and pushed&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="e8f7"&gt;Once the workflow went green, Claude Code built the Docker image and pushed it to ECR.&lt;/p&gt;
&lt;pre&gt;&lt;span id="d6cb"&gt;docker build -t myapp:v2.4.0 .&lt;br&gt;docker tag myapp:v2.4.0 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:v2.4.0&lt;br&gt;docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:v2.4.0&lt;/span&gt;&lt;/pre&gt;
&lt;p id="c793"&gt;Build time: three minutes. My involvement: zero.&lt;/p&gt;
&lt;h3 id="7b2a"&gt;&lt;strong&gt;Step 3: Manifest updated, ArgoCD synced to cluster&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="d13a"&gt;Claude Code updated the image tag in the Kubernetes deployment manifest and pushed it to the repo. ArgoCD detected the change and synced it to the cluster automatically.&lt;/p&gt;
&lt;pre&gt;&lt;span id="5de8"&gt;&lt;span&gt;# Claude Code updated this before pushing to Git&lt;/span&gt;&lt;br&gt;&lt;span&gt;spec:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;containers:&lt;/span&gt;&lt;br&gt;    &lt;span&gt;-&lt;/span&gt; &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;myapp&lt;/span&gt;&lt;br&gt;      &lt;span&gt;image:&lt;/span&gt; &lt;span&gt;123456789.&lt;/span&gt;&lt;span&gt;dkr.ecr.us-east-1.amazonaws.com/myapp:v2.4.0&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span id="c15b"&gt;&lt;span&gt;# ArgoCD sync status check&lt;/span&gt;&lt;br&gt;argocd app get myapp --refresh&lt;br&gt;argocd app &lt;span&gt;wait&lt;/span&gt; myapp --health&lt;/span&gt;&lt;/pre&gt;
&lt;p id="d262"&gt;Staging looked clean. Health checks passed. Two minutes of log monitoring, no anomalies. Moved to prod.&lt;/p&gt;
&lt;h3 id="1d70"&gt;&lt;strong&gt;Step 4: The moment I had to step in&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="f20f"&gt;Datadog flagged a latency spike mid-rollout response times jumping from 120ms to 800ms. Claude Code caught the alert, paused the rollout, and queued up a response. Here’s what it was about to run:&lt;/p&gt;
&lt;pre&gt;&lt;span id="86e7"&gt;&lt;span&gt;# This is what it queued — I cancelled it before it executed&lt;/span&gt;&lt;br&gt;kubectl scale deployment/myapp --replicas=2  &lt;span&gt;# was 4&lt;/span&gt;&lt;br&gt;kubectl rollout undo deployment/myapp&lt;/span&gt;&lt;/pre&gt;
&lt;p id="25ef"&gt;Scaling down during a latency spike is exactly backwards. It would’ve made things significantly worse. I cancelled the scale-down, let the rollback run on its own, and the latency resolved in under two minutes cold-start spike from the new image, totally normal for this service. Claude Code didn’t have that context. I did. That’s the job now.&lt;/p&gt;
&lt;h3 id="da43"&gt;&lt;strong&gt;Step 5: Slack summary, automatic&lt;/strong&gt;&lt;/h3&gt;
&lt;p id="1dbf"&gt;Once metrics stabilized, this landed in #deployments without me asking:&lt;/p&gt;
&lt;pre&gt;&lt;span id="1e0b"&gt;✅ myapp deploy v2.&lt;span&gt;4.0&lt;/span&gt; → rolled back &lt;span&gt;to&lt;/span&gt; v2.&lt;span&gt;3.8&lt;/span&gt;&lt;br&gt;&lt;span&gt;Reason:&lt;/span&gt; Latency spike detected (&lt;span&gt;120&lt;/span&gt;ms → &lt;span&gt;800&lt;/span&gt;ms)&lt;br&gt;Rollback time: &lt;span&gt;2&lt;/span&gt; minutes&lt;br&gt;&lt;span&gt;Next&lt;/span&gt; &lt;span&gt;step&lt;/span&gt;: Re-deploy &lt;span&gt;with&lt;/span&gt; extended warm-up period&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5369"&gt;Clean. Accurate. The kind of update that normally takes five minutes to write while you’re already stressed about something else.&lt;/p&gt;
&lt;p id="a35e"&gt;Total time from PR open to resolved rollback: twenty minutes. My actual involvement: cancelling two kubectl commands and making one judgment call about a cold-start spike.&lt;/p&gt;
&lt;p id="af23"&gt;That’s the workflow. Not magic just well-connected tooling with an agent in the middle that actually understands your stack.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A1TwIICIWlywd55pHq_bK3w.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="cdb6"&gt;The numbers (ecause vibes aren’t a deployment metric)&lt;/h2&gt;
&lt;p id="e217"&gt;Three months of this stack. Here’s what actually changed.&lt;/p&gt;
&lt;h3 id="a684"&gt;Deployment metrics&lt;/h3&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="351" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A5QdWnwFPK1A6wjEYszkDHw.png"&gt;&lt;p id="35a2"&gt;The deploy frequency jump is the one people don’t expect. When deployments are painful you batch changes to minimize how often you do them. When they’re automated and four minutes long you ship smaller, ship faster, and catch issues earlier. The whole engineering rhythm changes.&lt;/p&gt;
&lt;h3 id="d511"&gt;Monthly cost breakdown&lt;/h3&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="385" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AbJef5ty8fjIzIrTeIc4puA.png"&gt;&lt;h3 id="97ae"&gt;The ROI math&lt;/h3&gt;
&lt;p id="7b91"&gt;Thirty-six hours saved per month. If your hourly rate is $50, that’s $1,800 in reclaimed time every month. Spent $51 to get there.&lt;/p&gt;
&lt;p id="6b7b"&gt;That’s a 3,400% ROI. A dedicated DevOps hire at market rate runs $120K+ annually. This stack costs $612 a year and covers the bulk of what that role was doing the repetitive, process-heavy, script-running parts that consumed the most clock time.&lt;/p&gt;
&lt;p id="b82c"&gt;The parts it doesn’t cover architecture decisions, incident judgment calls, the scale-down situation from the last section those still need a human. But that’s maybe 20% of what the role actually looked like day to day.&lt;/p&gt;
&lt;p id="4c06"&gt;The honest version: the $120K number is provocative but not wrong. One senior engineer with this stack can operate infrastructure that previously needed a dedicated ops person. Whether that’s exciting or uncomfortable depends entirely on which side of that equation you’re sitting on.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="1735"&gt;Mistakes I made (so you don’t have to repeat them)&lt;/h2&gt;
&lt;p id="0011"&gt;Three months of building this taught me more through failures than wins. Here are the three that cost real time and one that almost cost a prod database.&lt;/p&gt;
&lt;h3 id="2f9b"&gt;Mistake 1: Trying to automate everything at once&lt;/h3&gt;
&lt;p id="579c"&gt;Week one I sat down and tried to wire up all seven tools over a single weekend. GitHub Actions, Docker, Kubernetes, Terraform, ArgoCD, Datadog, Slack all of it, simultaneously, from scratch.&lt;/p&gt;
&lt;p id="2f0c"&gt;By Sunday night I had seven half-working integrations, a broken cluster, and genuine regret.&lt;/p&gt;
&lt;p id="40a4"&gt;What actually worked was one tool at a time with a week of real usage before adding the next:&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="273" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A2FbOw5iVibrfBH4SsaKwVw.png"&gt;&lt;p id="7099"&gt;Each tool compounded on the last. By week ten the stack felt coherent because I actually understood every layer of it. The weekend approach would’ve given me a fragile house of cards I didn’t understand and couldn’t debug.&lt;/p&gt;
&lt;h3 id="0f7e"&gt;Mistake 2: Trusting AI-generated configs blindly&lt;/h3&gt;
&lt;p id="af9c"&gt;Claude Code once generated a Kubernetes deployment manifest with zero resource limits. Looked completely valid. Passed syntax checks. I deployed it without reading it carefully.&lt;/p&gt;
&lt;p id="00f1"&gt;It consumed all available cluster memory inside twenty minutes and took down two other services running on the same nodes.&lt;/p&gt;
&lt;pre&gt;&lt;span id="13e3"&gt;&lt;span&gt;# What Claude Code generated — notice what's missing&lt;/span&gt;&lt;br&gt;&lt;span&gt;spec:&lt;/span&gt;&lt;br&gt;  &lt;span&gt;containers:&lt;/span&gt;&lt;br&gt;    &lt;span&gt;-&lt;/span&gt; &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;myapp&lt;/span&gt;&lt;br&gt;      &lt;span&gt;image:&lt;/span&gt; &lt;span&gt;myapp:v1.2.0&lt;/span&gt;&lt;br&gt;      &lt;span&gt;ports:&lt;/span&gt;&lt;br&gt;        &lt;span&gt;-&lt;/span&gt; &lt;span&gt;containerPort:&lt;/span&gt; &lt;span&gt;3000&lt;/span&gt;&lt;br&gt;      &lt;span&gt;# No resources block. No limits. No requests.&lt;/span&gt;&lt;br&gt;      &lt;span&gt;# This will eat your cluster alive.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span id="06d4"&gt;&lt;span&gt;# What it should have included&lt;/span&gt;&lt;br&gt;      &lt;span&gt;resources:&lt;/span&gt;&lt;br&gt;        &lt;span&gt;requests:&lt;/span&gt;&lt;br&gt;          &lt;span&gt;memory:&lt;/span&gt; &lt;span&gt;"256Mi"&lt;/span&gt;&lt;br&gt;          &lt;span&gt;cpu:&lt;/span&gt; &lt;span&gt;"250m"&lt;/span&gt;&lt;br&gt;        &lt;span&gt;limits:&lt;/span&gt;&lt;br&gt;          &lt;span&gt;memory:&lt;/span&gt; &lt;span&gt;"512Mi"&lt;/span&gt;&lt;br&gt;          &lt;span&gt;cpu:&lt;/span&gt; &lt;span&gt;"500m"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="0d9e"&gt;The same thing happened with a Terraform config that referenced a resource argument that doesn’t exist in the current provider version generated confidently, failed on apply. Always run &lt;code&gt;terraform plan&lt;/code&gt;. Always read the Kubernetes manifest before &lt;code&gt;kubectl apply&lt;/code&gt;. The AI doesn't know what it doesn't know and it won't flag its own blind spots.&lt;/p&gt;
&lt;h3 id="97e9"&gt;Mistake 3: No rollback plan in the early setup&lt;/h3&gt;
&lt;p id="53f1"&gt;First two months I had automated deployments but no automated rollback trigger. Which meant a bad deploy looked like this: automated push to prod, health checks fail, panic, manual kubectl rollout undo, five minutes of scrambling that felt like thirty.&lt;/p&gt;
&lt;p id="5e8b"&gt;&lt;strong&gt;The fix was simple and I should have built it on day one:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="697d"&gt;&lt;span&gt;# Added to every GitHub Actions deploy job&lt;/span&gt;&lt;br&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;Verify&lt;/span&gt; &lt;span&gt;deployment&lt;/span&gt; &lt;span&gt;health&lt;/span&gt;&lt;br&gt;  &lt;span&gt;run:&lt;/span&gt; &lt;span&gt;|&lt;br&gt;    kubectl rollout status deployment/myapp --timeout=120s || &amp;lt;br&amp;gt;    (kubectl rollout undo deployment/myapp &amp;amp;&amp;amp; exit 1)&lt;br&gt;&lt;/span&gt;&lt;br&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;name:&lt;/span&gt; &lt;span&gt;Post&lt;/span&gt; &lt;span&gt;rollback&lt;/span&gt; &lt;span&gt;notice&lt;/span&gt; &lt;span&gt;to&lt;/span&gt; &lt;span&gt;Slack&lt;/span&gt;&lt;br&gt;  &lt;span&gt;if:&lt;/span&gt; &lt;span&gt;failure()&lt;/span&gt;&lt;br&gt;  &lt;span&gt;run:&lt;/span&gt; &lt;span&gt;|&lt;br&gt;    curl -X POST &lt;a href="https://slack.com/api/chat.postMessage" rel="noopener noreferrer"&gt;https://slack.com/api/chat.postMessage&lt;/a&gt; &amp;lt;br&amp;gt;      -H "Authorization: Bearer $SLACK_TOKEN" &amp;lt;br&amp;gt;      -d "channel=#deployments" &amp;lt;br&amp;gt;      -d "text=⚠️ Deploy failed. Auto-rolled back to previous version."&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="018a"&gt;Every deployment now has an automatic rollback trigger if health checks don’t pass within two minutes. Slack gets notified either way. I find out what happened from a clean summary, not from a user complaint.&lt;/p&gt;
&lt;p id="d065"&gt;The pattern across all three mistakes is the same: moving faster than your understanding. The stack rewards patience. Add one thing, break it, understand why, fix it, then add the next thing. The engineers who try to skip that process are the ones who end up with automation they’re afraid to touch.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="ef57"&gt;The DevOps role isn’t dying. It’s collapsing into you.&lt;/h2&gt;
&lt;p id="d4f4"&gt;Here’s the take: the traditional DevOps engineer role the one that’s mostly deployment pipelines, manual runbooks, and being the person who knows which kubectl flag to toggle at midnight that role is getting absorbed into the senior engineer. The skills aren’t disappearing. They’re becoming table stakes for everyone who ships software.&lt;/p&gt;
&lt;p id="9147"&gt;Automation doesn’t eliminate work. It eliminates bullshit.&lt;/p&gt;
&lt;p id="258b"&gt;I still write code. I still handle incidents. I still make judgment calls like the scale-down situation that would’ve made a latency spike significantly worse. What I don’t do anymore is spend nine hours a week babysitting deployment scripts that were never reliable enough to trust unsupervised anyway.&lt;/p&gt;
&lt;p id="cee6"&gt;The $120K number in the title is real but it’s also the wrong frame. This isn’t about replacing a person. It’s about finally treating your deployment workflow like a system worth designing instead of a chore worth tolerating. One engineer with this stack can operate infrastructure that previously needed a dedicated ops role. Whether that’s exciting or uncomfortable depends on where you’re sitting.&lt;/p&gt;
&lt;p id="71ed"&gt;What comes next is more interesting full IAM integration, end-to-end autonomous deploys, agents with scoped cloud permissions handling routine operations without a human in the approval chain. Some teams are already there. Most aren’t ready for the conversation about who owns the incident when the agent breaks something.&lt;/p&gt;
&lt;p id="8e1e"&gt;I don’t have a clean answer for that yet. I suspect nobody does.&lt;/p&gt;
&lt;p id="b941"&gt;Drop your current deployment setup or your hot take in the comments. Especially curious how teams are handling the governance question because that’s the conversation the industry is quietly avoiding.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="c287"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="2dc2"&gt;&lt;a href="https://docs.anthropic.com/en/docs/claude-code" rel="noopener ugc nofollow noreferrer"&gt;Claude Code docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="cda3"&gt;&lt;a href="https://docs.github.com/en/actions" rel="noopener ugc nofollow noreferrer"&gt;GitHub Actions docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="785f"&gt;&lt;a href="https://registry.terraform.io/" rel="noopener ugc nofollow noreferrer"&gt;Terraform Registry&lt;/a&gt;&lt;/li&gt;

&lt;li id="10c7"&gt;&lt;a href="https://kubernetes.io/docs/home/" rel="noopener ugc nofollow noreferrer"&gt;Kubernetes docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="85c3"&gt;&lt;a href="https://docs.docker.com/develop/dev-best-practices/" rel="noopener ugc nofollow noreferrer"&gt;Docker best practices&lt;/a&gt;&lt;/li&gt;

&lt;li id="ce5a"&gt;&lt;a href="https://argo-cd.readthedocs.io/en/stable/" rel="noopener ugc nofollow noreferrer"&gt;ArgoCD docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="4883"&gt;&lt;a href="https://docs.datadoghq.com/integrations/" rel="noopener ugc nofollow noreferrer"&gt;Datadog integrations&lt;/a&gt;&lt;/li&gt;

&lt;li id="8413"&gt;&lt;a href="https://www.reddit.com/r/devops/" rel="noopener ugc nofollow noreferrer"&gt;r/devops&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>claude</category>
      <category>devops</category>
    </item>
    <item>
      <title>Four AWS VPC blueprints that will save your MLOps pipeline</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Fri, 17 Apr 2026 03:19:23 +0000</pubDate>
      <link>https://forem.com/dev_tips/four-aws-vpc-blueprints-that-will-save-your-mlops-pipeline-5d04</link>
      <guid>https://forem.com/dev_tips/four-aws-vpc-blueprints-that-will-save-your-mlops-pipeline-5d04</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="ef25"&gt;&lt;/h1&gt;
&lt;h2 id="8b56"&gt;Four VPC blueprints for MLOps from scrappy MVP to distributed LLM training, with the cost traps nobody puts in the tutorial.&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;p id="415b"&gt;You spent three weeks tuning that model. The loss curve is clean, the eval metrics are holding, and someone just Slacked you “when can we ship this?” The answer should be soon. Instead you’re staring at a SageMaker training job stuck in Pending, a NAT Gateway line item that looks like a mortgage payment, and a security audit finding that says your training data touched the public internet.&lt;/p&gt;
&lt;p id="8790"&gt;The model was never the problem. The network was.&lt;/p&gt;
&lt;p id="11ba"&gt;AWS VPC is the part of MLOps that doesn’t make it into the YouTube tutorials. It’s not exciting. Nobody claps for a well-architected private subnet. But get it wrong and you pay in three currencies simultaneously security incidents, compliance failures, and cloud bills that make your manager ask questions in a tone you don’t like.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="d262"&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; This article walks through four real VPC blueprints for ML workloads on AWS MVP experimentation, tabular data pipelines, multi-modal training, and distributed LLM training. Each one covers the network config, a CLI deployment snippet, and the specific cost or security trap that comes with it. Cloud comparison at the end.&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="5460"&gt;VPC anatomy what you actually need to know&lt;/h2&gt;
&lt;p id="4dba"&gt;Think of AWS as a massive shared data center where thousands of companies run workloads on the same physical hardware. A VPC is your private office inside that building your own IP ranges, your own routing rules, your own walls. Nobody else’s traffic touches yours. Nobody can reach your model weights unless you explicitly let them.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AII8NQKVExnKK2yfnvZ2K9A.png"&gt;&lt;p id="0a5f"&gt;&lt;strong&gt;Five components do most of the work:&lt;/strong&gt;&lt;/p&gt;
&lt;p id="e8ba"&gt;&lt;strong&gt;Subnets&lt;/strong&gt; divide your VPC into zones. Public subnets have a direct route to the internet gateway load balancers and bastion hosts live here. Private subnets have no direct internet route your training jobs, inference endpoints, and data pipelines live here. The rule is simple: if it touches real data, it belongs in a private subnet.&lt;/p&gt;
&lt;p id="b0e0"&gt;&lt;strong&gt;Route tables&lt;/strong&gt; are the traffic cops. Each subnet has one, and it decides where packets go out through the internet gateway, through a NAT gateway, or nowhere.&lt;/p&gt;
&lt;p id="ea36"&gt;&lt;strong&gt;Gateways&lt;/strong&gt; are the entry and exit points. Internet Gateway for public traffic. NAT Gateway for outbound-only private traffic pip installs, OS patches, Docker pulls. VPN Gateway for encrypted tunnels back to your corporate data center.&lt;/p&gt;
&lt;p id="ef27"&gt;&lt;strong&gt;VPC endpoints&lt;/strong&gt; let private resources talk directly to AWS services S3, DynamoDB, SageMaker APIs without touching the public internet or paying NAT egress fees. Gateway endpoints for S3 and DynamoDB are free. Interface endpoints for everything else cost ~$0.01/hr but eliminate far more expensive NAT traffic.&lt;/p&gt;
&lt;p id="299b"&gt;&lt;strong&gt;Security groups&lt;/strong&gt; are stateful instance-level firewalls. Network ACLs are stateless subnet-level filters. You need both in production.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2Al7VfRZ6LA-MOJtRpGEc1yA.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="fe22"&gt;Use case 1: MVP &amp;amp; rapid experimentation&lt;/h2&gt;
&lt;p id="d77f"&gt;Every ML project starts the same way. You have a hypothesis, a dataset, and the dangerous combination of a fresh AWS account and zero infrastructure opinions. The goal is to validate the idea before anyone asks hard questions. That’s correct. You should not be building a production-grade VPC on day one of an experiment.&lt;/p&gt;
&lt;p id="657e"&gt;But there’s a difference between moving fast and leaving the front door open.&lt;/p&gt;
&lt;p id="c269"&gt;The MVP setup is one public subnet, one EC2 instance running a SageMaker Notebook, one internet gateway for pulling packages from GitHub and PyPI, and one VPC endpoint pointing straight at S3. That’s the whole diagram.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2Aatuz78UHQiGzHgv12xBkrA.png"&gt;&lt;p id="754b"&gt;The S3 endpoint is the one thing you do not skip even here. Without it every byte your notebook reads from or writes to S3 travels through the internet gateway and back. You pay NAT-equivalent egress on every model checkpoint and dataset pull. The endpoint is free, routes internally, and takes five minutes to set up.&lt;/p&gt;
&lt;pre&gt;&lt;span id="a9f9"&gt;&lt;span&gt;# create vpc and public subnet&lt;/span&gt;&lt;br&gt;&lt;span&gt;VPC_ID&lt;/span&gt;=&lt;span&gt;$(&lt;/span&gt;aws ec2 create-vpc --cidr-block &lt;span&gt;172.16&lt;/span&gt;.&lt;span&gt;0.0&lt;/span&gt;/&lt;span&gt;16&lt;/span&gt; &amp;lt;br&amp;gt;  --query &lt;span&gt;'Vpc.VpcId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;&lt;span&gt;SUBNET_ID&lt;/span&gt;=&lt;span&gt;$(&lt;/span&gt;aws ec2 create-subnet &amp;lt;br&amp;gt;  --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; --cidr-block &lt;span&gt;172.16&lt;/span&gt;.&lt;span&gt;0.0&lt;/span&gt;/&lt;span&gt;24&lt;/span&gt; &amp;lt;br&amp;gt;  --query &lt;span&gt;'Subnet.SubnetId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;&lt;span&gt;# attach internet gateway&lt;/span&gt;&lt;br&gt;&lt;span&gt;IGW_ID&lt;/span&gt;=&lt;span&gt;$(&lt;/span&gt;aws ec2 create-internet-gateway &amp;lt;br&amp;gt;  --query &lt;span&gt;'InternetGateway.InternetGatewayId'&lt;/span&gt; --output text)&lt;br&gt;aws ec2 attach-internet-gateway &amp;lt;br&amp;gt;  --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; --internet-gateway-id &lt;span&gt;$IGW_ID&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# create free s3 gateway endpoint&lt;/span&gt;&lt;br&gt;aws ec2 create-vpc-endpoint &amp;lt;br&amp;gt;  --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --service-name com.amazonaws.&lt;span&gt;$REGION&lt;/span&gt;.s3 &amp;lt;br&amp;gt;  --route-table-ids &lt;span&gt;$ROUTE_TABLE&lt;/span&gt;_ID&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8ae2"&gt;The public subnet is acceptable during experimentation because the blast radius of a mistake is small and the iteration speed benefit is real. The moment actual customer data or proprietary datasets enter the picture, this config has to go. You don’t graduate from MVP by training a better model you graduate by moving to a private subnet.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AyhDb5RyhOT9wtv1LXOSjLg.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="be1d"&gt;Use case 2: Tabular data pipelines&lt;/h2&gt;
&lt;p id="55cb"&gt;Tabular data pipelines are where the security stakes get real. You’re processing CSV and Parquet files containing customer records, financial transactions, or healthcare data. HIPAA, PCI, SOC2 pick your compliance requirement. None of them are happy with your training data transiting a public subnet.&lt;/p&gt;
&lt;p id="d4f5"&gt;The config shift is straightforward: everything moves into a private subnet. No public IPs, no internet gateway route, no direct inbound traffic from anywhere. Your EC2 instances pull data from S3 and DynamoDB through free gateway endpoints. Your corporate data center connects through a VPN Gateway over an encrypted tunnel. Nothing touches the public internet at any point in the pipeline.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AVQKxajnJ1Gqw4E3AhH1brA.png"&gt;&lt;pre&gt;&lt;span id="cc74"&gt;&lt;span&gt;# create private subnet — no public ip mapping&lt;/span&gt;&lt;br&gt;PRIVATE_SUBNET_ID=$(aws ec2 create-subnet &amp;lt;br&amp;gt;  --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; --cidr-block 172.16.1.0/24 &amp;lt;br&amp;gt;  --query &lt;span&gt;'Subnet.SubnetId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;&lt;span&gt;# create dedicated route table for private subnet&lt;/span&gt;&lt;br&gt;PRIVATE_RTB_ID=$(aws ec2 create-route-table &amp;lt;br&amp;gt;  --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --query &lt;span&gt;'RouteTable.RouteTableId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;aws ec2 associate-route-table &amp;lt;br&amp;gt;  --subnet-id &lt;span&gt;$PRIVATE_SUBNET_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --route-table-id &lt;span&gt;$PRIVATE_RTB_ID&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# create free gateway endpoints for s3 and dynamodb&lt;/span&gt;&lt;br&gt;&lt;span&gt;for&lt;/span&gt; SERVICE &lt;span&gt;in&lt;/span&gt; s3 dynamodb; &lt;span&gt;do&lt;/span&gt;&lt;br&gt;  aws ec2 create-vpc-endpoint &amp;lt;br&amp;gt;    --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; &amp;lt;br&amp;gt;    --service-name com.amazonaws.&lt;span&gt;$REGION&lt;/span&gt;.&lt;span&gt;$SERVICE&lt;/span&gt; &amp;lt;br&amp;gt;    --route-table-ids &lt;span&gt;$PRIVATE_RTB_ID&lt;/span&gt;&lt;br&gt;&lt;span&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="a747"&gt;Two things worth noting here. First, gateway endpoints for S3 and DynamoDB charge zero hourly fees they work by injecting a prefix list entry into your route table, so traffic routes internally at no cost. Second, the VPN Gateway enables your on-prem databases to feed the pipeline directly without exposing raw data to the internet at any hop. For batch processing workloads that run intermittently, this setup is both the most secure and the most cost-effective option available.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2AQBwX5oKbXiKItE7uEhi5Gg.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="e431"&gt;Use case 3: multi-modal training&lt;/h2&gt;
&lt;p id="1aac"&gt;Multi-modal training is where VPC configuration gets genuinely interesting. You’re ingesting video, audio, LiDAR, and text simultaneously, routing them through high-frequency API calls to services like Amazon Rekognition, and pushing inference results back out to consumers. The data volumes are large, the API call frequency is high, and a poorly designed network will make both your latency and your NAT bill hurt.&lt;/p&gt;
&lt;p id="af18"&gt;The core problem with naive setups here is NAT congestion. If every API call to Rekognition, every SageMaker runtime request, and every S3 read routes through a single NAT Gateway, you’ve created a bottleneck and a billing disaster simultaneously. The fix is interface endpoints one for each AWS service your training cluster talks to frequently. NAT handles only the small external pulls it was designed for. Everything internal stays internal.&lt;/p&gt;
&lt;pre&gt;&lt;span id="3c4c"&gt;&lt;span&gt;# create nat gateway in public subnet for external traffic&lt;/span&gt;&lt;br&gt;ALLOCATION_ID=$(aws ec2 allocate-address &amp;lt;br&amp;gt;  --domain vpc --query &lt;span&gt;'AllocationId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;NAT_GW_ID=$(aws ec2 create-nat-gateway &amp;lt;br&amp;gt;  --subnet-id &lt;span&gt;$PUBLIC_SUBNET_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --allocation-id &lt;span&gt;$ALLOCATION_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --query &lt;span&gt;'NatGateway.NatGatewayId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;&lt;span&gt;# route private subnet outbound traffic through nat&lt;/span&gt;&lt;br&gt;aws ec2 create-route &amp;lt;br&amp;gt;  --route-table-id &lt;span&gt;$PRIVATE_RTB_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --destination-cidr-block 0.0.0.0/0 &amp;lt;br&amp;gt;  --nat-gateway-id &lt;span&gt;$NAT_GW_ID&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# create interface endpoints for high-frequency aws services&lt;/span&gt;&lt;br&gt;SERVICES=(&lt;span&gt;"sagemaker.runtime"&lt;/span&gt; &lt;span&gt;"sagemaker.api"&lt;/span&gt; &lt;span&gt;"rekognition"&lt;/span&gt;)&lt;br&gt;&lt;span&gt;for&lt;/span&gt; SERVICE &lt;span&gt;in&lt;/span&gt; &lt;span&gt;"&lt;span&gt;${SERVICES[@]}&lt;/span&gt;"&lt;/span&gt;; &lt;span&gt;do&lt;/span&gt;&lt;br&gt;  aws ec2 create-vpc-endpoint &amp;lt;br&amp;gt;    --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; &amp;lt;br&amp;gt;    --vpc-endpoint-type Interface &amp;lt;br&amp;gt;    --service-name com.amazonaws.&lt;span&gt;$REGION&lt;/span&gt;.&lt;span&gt;$SERVICE&lt;/span&gt; &amp;lt;br&amp;gt;    --subnet-ids &lt;span&gt;$PRIVATE_SUBNET_ID&lt;/span&gt; &amp;lt;br&amp;gt;    --security-group-ids &lt;span&gt;$SG_ID&lt;/span&gt; &amp;lt;br&amp;gt;    --private-dns-enabled&lt;br&gt;&lt;span&gt;done&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="94ef"&gt;The private DNS flag on interface endpoints is the detail that catches teams out. Without it your application code still resolves the public DNS hostname for Rekognition or SageMaker and routes traffic through NAT anyway completely defeating the point. With it enabled, the private endpoint intercepts those DNS calls automatically and keeps traffic on the internal backbone.&lt;/p&gt;
&lt;p id="b5f9"&gt;The training cluster stays in a private subnet with no direct internet exposure. Inference results that need to reach external consumers route outbound through NAT. Everything else model artifacts, feature store reads, API calls to AWS services never leaves the AWS network.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2ArybyF55JmLG_c4QC3KJkQQ.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="c562"&gt;Use case 4: distributed LLM training&lt;/h2&gt;
&lt;p id="6937"&gt;Distributed LLM training is the use case where every network decision gets amplified by the number of GPUs involved. You’re running p4d.24xlarge or p5 instances, synchronizing gradients across nodes using NCCL or MPI, and the latency between those nodes directly impacts your training throughput. Standard TCP/IP networking is not fast enough. This is where Elastic Fabric Adapter (EFA) enters the picture.&lt;/p&gt;
&lt;p id="6acc"&gt;EFA bypasses the OS network stack entirely and enables direct memory-to-memory communication between instances at microsecond-level latency. The catch is that EFA only works between instances in the same subnet, same availability zone, and same placement group. Your VPC layout has to be built around that constraint from the start you cannot bolt it on later.&lt;/p&gt;
&lt;p id="6f1d"&gt;The security group configuration is equally important and equally non-obvious. Distributed training nodes need to communicate freely with each other NCCL all-reduce operations, MPI coordination messages, checkpoint syncing. The pattern that works is a self-referencing security group: every node allows all ingress and egress traffic from instances that share the same security group. Nothing from outside the group gets in. Everything inside communicates freely.&lt;/p&gt;
&lt;pre&gt;&lt;span id="11b8"&gt;&lt;span&gt;# create security group for efa-enabled training cluster&lt;/span&gt;&lt;br&gt;SG_ID=$(aws ec2 create-security-group &amp;lt;br&amp;gt;  --group-name &lt;span&gt;"llm-training-sg"&lt;/span&gt; &amp;lt;br&amp;gt;  --description &lt;span&gt;"EFA and NCCL communication"&lt;/span&gt; &amp;lt;br&amp;gt;  --vpc-id &lt;span&gt;$VPC_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --query &lt;span&gt;'GroupId'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;&lt;span&gt;# self-referencing rules - nodes talk freely to each other&lt;/span&gt;&lt;br&gt;aws ec2 authorize-security-group-ingress &amp;lt;br&amp;gt;  --group-id &lt;span&gt;$SG_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --protocol all --port -1 &amp;lt;br&amp;gt;  --source-group &lt;span&gt;$SG_ID&lt;/span&gt;&lt;br&gt;aws ec2 authorize-security-group-egress &amp;lt;br&amp;gt;  --group-id &lt;span&gt;$SG_ID&lt;/span&gt; &amp;lt;br&amp;gt;  --protocol all --port -1 &amp;lt;br&amp;gt;  --source-group &lt;span&gt;$SG_ID&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;# create placement group for maximum efa performance&lt;/span&gt;&lt;br&gt;PG_NAME=$(aws ec2 create-placement-group &amp;lt;br&amp;gt;  --group-name &lt;span&gt;"llm-training-pg"&lt;/span&gt; &amp;lt;br&amp;gt;  --strategy cluster &amp;lt;br&amp;gt;  --query &lt;span&gt;'PlacementGroup.GroupName'&lt;/span&gt; --output text)&lt;br&gt;&lt;br&gt;&lt;span&gt;# launch efa-enabled training instance&lt;/span&gt;&lt;br&gt;aws ec2 run-instances &amp;lt;br&amp;gt;  --instance-type p4d.24xlarge &amp;lt;br&amp;gt;  --placement &lt;span&gt;"GroupName=&lt;span&gt;$PG_NAME&lt;/span&gt;"&lt;/span&gt; &amp;lt;br&amp;gt;  --network-interfaces &amp;lt;br&amp;gt;  &lt;span&gt;"DeviceIndex=0,InterfaceType=efa,Groups=&lt;span&gt;$SG_ID&lt;/span&gt;,SubnetId=&lt;span&gt;$TRAIN_SUBNET_ID&lt;/span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="fff4"&gt;The placement group strategy cluster packs instances physically close together inside AWS’s infrastructure this is what delivers the low-latency interconnect EFA needs to hit its performance ceiling. Skip the placement group and your gradient sync latency climbs, your GPU utilization drops, and your training run costs more for the same result.&lt;/p&gt;
&lt;p id="f7fe"&gt;One final thing: SageMaker’s managed training with EFA requires your VPC to have endpoints for S3, SageMaker API, SageMaker runtime, CloudWatch Logs, and ECR. Miss any one of them and the job queues indefinitely. Add them all upfront and you never think about it again.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="436" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A945%2F1%2A7fbaioGkxVB7Gb1kPpuZOg.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="43ff"&gt;Cost traps nobody warns you about (208 words)&lt;/h2&gt;
&lt;p id="b214"&gt;You did everything right. Private subnets, EFA cluster, VPN gateway, interface endpoints on the high-frequency services. The architecture diagram looks like it belongs in an AWS re:Invent slide deck. Then the bill arrives.&lt;/p&gt;
&lt;p id="4ddb"&gt;NAT Gateway is the villain in every act. It charges you two ways simultaneously and most engineers only notice one. The hourly rate $0.045 per gateway per hour runs whether your training jobs are active or not. Provision one per availability zone for redundancy and three idle gateways are quietly billing you $97/month before a single byte moves through them. Then the per-GB processing fee hits on top: $0.045 per GB of traffic. A training job pulling a 50GB dataset ten times a day generates $22.50 in NAT fees alone, daily, before compute costs enter the conversation.&lt;/p&gt;
&lt;p id="b284"&gt;The fix is almost always more VPC endpoints. Gateway endpoints for S3 and DynamoDB are completely free and eliminate the largest NAT traffic sources for most ML workloads. Interface endpoints for SageMaker APIs, ECR, and CloudWatch cost around $0.01/hr each but remove traffic that was costing multiples of that through NAT.&lt;/p&gt;
&lt;p id="2821"&gt;Three more traps worth naming. First, idle interface endpoints still bill hourly audit quarterly and delete anything nothing is actively using. Second, cross-AZ data transfer fees apply when your private subnet instances in one availability zone talk to endpoints or resources in another replicate endpoints per AZ or pay the transfer tax. Third, multi-AZ NAT Gateway setups double or triple your hourly gateway costs — size this deliberately against your actual redundancy requirements rather than provisioning by default.&lt;/p&gt;
&lt;p id="9678"&gt;&lt;a href="https://aws.amazon.com/aws-cost-management/aws-cost-explorer/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;AWS Cost Explorer&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;with VPC resource tagging surfaces all of this before it compounds. Set it up on day one.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="44b3"&gt;Cloud comparison&lt;/h2&gt;
&lt;p id="af32"&gt;Before you commit fully to AWS VPC, it’s worth knowing what the alternatives look like because the architectural differences between providers are real and affect how you design ML systems at scale.&lt;/p&gt;
&lt;p id="fa4c"&gt;AWS VPC is regional and availability-zone-aware. You get granular control over subnets, routing, and security at the AZ level, which makes fault isolation clean and deliberate. The tradeoff is complexity — multi-region architectures require VPC peering or Transit Gateway and add meaningful operational overhead.&lt;/p&gt;
&lt;p id="30ec"&gt;GCP VPC is global by default. A single VPC spans all regions, which simplifies cross-region communication and global load balancing significantly. For ML teams running distributed training across geographies or serving inference globally, this reduces the networking boilerplate considerably. The downside is less granular regional isolation out of the box.&lt;/p&gt;
&lt;p id="9cbd"&gt;Azure VNet is regional like AWS but integrates tightly with the Microsoft ecosystem Active Directory, hybrid identity, and enterprise compliance tooling. For teams already running on Azure DevOps or with heavy Windows Server dependencies on-prem, the VPN and ExpressRoute integrations feel native in a way AWS and GCP don’t match.&lt;/p&gt;
&lt;p id="06af"&gt;The honest verdict: if your ML stack is already AWS-native SageMaker, S3, DynamoDB, ECR there is no compelling reason to look elsewhere for networking. If you’re greenfield and globally distributed, GCP’s flat network model saves real architectural complexity.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="3b0b"&gt;Conclusion&lt;/h2&gt;
&lt;p id="0aeb"&gt;AWS VPC is not the exciting part of MLOps. Nobody puts “designed a secure multi-AZ EFA cluster with self-referencing security groups” in their Twitter bio. But the four blueprints above cover the full progression most ML teams go through usually in the wrong order, usually with a surprise bill somewhere in the middle.&lt;/p&gt;
&lt;p id="0b9c"&gt;Start lean. Graduate deliberately. Replace NAT with endpoints wherever AWS lets you. And for distributed LLM training, get the placement group and EFA config right before you launch a single p4d instance not after your third failed training run.&lt;/p&gt;
&lt;p id="d598"&gt;Build the model. Build the walls. In that order.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="4700"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="24f3"&gt;&lt;a href="https://docs.aws.amazon.com/vpc/latest/userguide/what-is-amazon-vpc.html" rel="noopener ugc nofollow noreferrer"&gt;AWS VPC documentation&lt;/a&gt;&lt;/li&gt;

&lt;li id="3113"&gt;&lt;a href="https://docs.aws.amazon.com/sagemaker/latest/dg/train-vpc.html" rel="noopener ugc nofollow noreferrer"&gt;SageMaker training jobs in a VPC&lt;/a&gt;&lt;/li&gt;

&lt;li id="f81d"&gt;&lt;a href="https://docs.aws.amazon.com/sagemaker/latest/dg/interface-vpc-endpoint.html" rel="noopener ugc nofollow noreferrer"&gt;VPC endpoints for SageMaker&lt;/a&gt;&lt;/li&gt;

&lt;li id="5ff0"&gt;&lt;a href="https://aws.amazon.com/vpc/pricing/" rel="noopener ugc nofollow noreferrer"&gt;AWS NAT Gateway pricing&lt;/a&gt;&lt;/li&gt;

&lt;li id="dd8d"&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/efa.html" rel="noopener ugc nofollow noreferrer"&gt;EFA documentation&lt;/a&gt;&lt;/li&gt;

&lt;li id="058f"&gt;&lt;a href="https://docs.aws.amazon.com/wellarchitected/latest/machine-learning-lens/welcome.html" rel="noopener ugc nofollow noreferrer"&gt;AWS Well-Architected ML Lens&lt;/a&gt;&lt;/li&gt;

&lt;li id="4082"&gt;&lt;a href="https://cloud.google.com/vpc/docs/overview" rel="noopener ugc nofollow noreferrer"&gt;GCP VPC overview&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>aws</category>
      <category>cicd</category>
      <category>programming</category>
      <category>devops</category>
    </item>
    <item>
      <title>10 Claude Code commands that actually changed how I ship</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Thu, 16 Apr 2026 07:02:51 +0000</pubDate>
      <link>https://forem.com/dev_tips/10-claude-code-commands-that-actually-changed-how-i-ship-45oj</link>
      <guid>https://forem.com/dev_tips/10-claude-code-commands-that-actually-changed-how-i-ship-45oj</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h2 id="fb3e"&gt;I was re-typing the same prompts from memory every day. Then I found out Claude Code had a whole command system I’d been ignoring for months.&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;p id="fd79"&gt;You know that moment when you realize the tool you’ve been using for half a year has a feature that would’ve saved you hundreds of hours and it was in the docs the whole time? &lt;strong&gt;&lt;em&gt;Yeah. That’s this article.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p id="51e2"&gt;I’d been running Claude Code daily since early 2025. Writing prompts, getting code, copy-pasting, repeat. Classic vibe coding loop. What I didn’t realize was that I was basically driving a sports car in first gear the entire time. Every code review, I’d retype the same 12-line checklist. Every new component, same scaffold instructions. Every commit, same “write me a conventional commit message” prompt from memory slightly different each time, slightly worse output each time. Turns out there’s a name for that: prompt drift. And it quietly tanks your results without you ever noticing.&lt;/p&gt;
&lt;p id="ee09"&gt;Claude Code has a slash command system that lets you save any prompt as a reusable command, version-control it with your team via Git, and fire it with a single &lt;code&gt;/command-name&lt;/code&gt;. It's been there the whole time. Most devs skip it because nobody told them it existed.&lt;/p&gt;
&lt;p id="0d95"&gt;So this is that article. No fluff, no “AI will 10x your productivity” nonsense just 10 commands, what they actually do, real code you can drop into your project today, and the specific moments they saved me from myself.&lt;/p&gt;
&lt;p id="6565"&gt;&lt;strong&gt;Here’s what’s coming:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="a292"&gt;How slash commands work (and the 4-minute setup you’ll actually do)&lt;/li&gt;

&lt;li id="bfa3"&gt;

&lt;strong&gt;Commands 1–3: &lt;/strong&gt;The daily drivers you’ll use every single session&lt;/li&gt;

&lt;li id="f8d4"&gt;

&lt;strong&gt;Commands 4–6:&lt;/strong&gt; The workflow multipliers that kill repetitive grunt work&lt;/li&gt;

&lt;li id="2e23"&gt;

&lt;strong&gt;Commands 7–9:&lt;/strong&gt; The power moves most devs don’t know exist&lt;/li&gt;

&lt;li id="a841"&gt;

&lt;strong&gt;Command 10: &lt;/strong&gt;The team play that ended merge conflicts for a full sprint&lt;/li&gt;

&lt;li id="8479"&gt;

&lt;strong&gt;Resources&lt;/strong&gt;, &lt;strong&gt;links&lt;/strong&gt;, and the full repo to steal everything&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="d7b1"&gt;&lt;strong&gt;Let’s go.&lt;/strong&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="b700"&gt;How slash commands actually work (set this up first)&lt;/h2&gt;
&lt;p id="9e86"&gt;Before the commands, two minutes of context because this tripped me up early and I don’t want it to trip you up.&lt;/p&gt;
&lt;p id="e0d3"&gt;Claude Code has two different things that look identical but aren’t: &lt;strong&gt;built-in slash commands&lt;/strong&gt; and &lt;strong&gt;custom slash commands&lt;/strong&gt;. Built-ins are hardcoded into the CLI things like &lt;code&gt;/clear&lt;/code&gt;, &lt;code&gt;/compact&lt;/code&gt;, &lt;code&gt;/help&lt;/code&gt;, &lt;code&gt;/diff&lt;/code&gt;. They just exist. Custom commands are markdown files you create yourself that become slash commands. That's the system we're mostly talking about today.&lt;/p&gt;
&lt;p id="b663"&gt;&lt;strong&gt;To create a custom command, you drop a &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;.md&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; file into one of two folders:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="8d89"&gt;&lt;span&gt;# Project-scoped — shared with your team via Git&lt;/span&gt;&lt;br&gt;your-repo/.claude/commands/preflight.md   →   /preflight&lt;br&gt;&lt;span&gt;# User-scoped - personal, works across all your projects&lt;/span&gt;&lt;br&gt;~/.claude/commands/orient.md              →   /orient&lt;br&gt;&lt;span&gt;# Subdirectories create prefixed commands&lt;/span&gt;&lt;br&gt;.claude/commands/db/migrate.md            →   /db:migrate&lt;/span&gt;&lt;/pre&gt;
&lt;p id="abe4"&gt;The filename becomes the command name. The file content becomes the prompt. That’s the whole system. Stupidly simple once you see it.&lt;/p&gt;
&lt;p id="1141"&gt;You can optionally add YAML frontmatter at the top to pre-approve tools (so Claude stops asking permission on every &lt;code&gt;git&lt;/code&gt; call), pin a model, or add a description:&lt;/p&gt;
&lt;pre&gt;&lt;span id="d0e8"&gt;&lt;span&gt;---&lt;/span&gt;&lt;br&gt;&lt;span&gt;description:&lt;/span&gt; &lt;span&gt;Pre-commit&lt;/span&gt; &lt;span&gt;check&lt;/span&gt; &lt;span&gt;for&lt;/span&gt; &lt;span&gt;debug&lt;/span&gt; &lt;span&gt;artifacts&lt;/span&gt; &lt;span&gt;and&lt;/span&gt; &lt;span&gt;code&lt;/span&gt; &lt;span&gt;smells&lt;/span&gt;&lt;br&gt;&lt;span&gt;allowed-tools:&lt;/span&gt; &lt;span&gt;Bash(git&lt;/span&gt; &lt;span&gt;&lt;em&gt;),&lt;/em&gt;&lt;/span&gt; &lt;span&gt;Bash(grep&lt;/span&gt; &lt;span&gt;),&lt;/span&gt; &lt;span&gt;Read,&lt;/span&gt; &lt;span&gt;Glob&lt;/span&gt;&lt;br&gt;&lt;span&gt;model:&lt;/span&gt; &lt;span&gt;claude-sonnet-4-6&lt;/span&gt;&lt;br&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="f1e7"&gt;&lt;strong&gt;And if you need dynamic input like passing a filename or a ticket number use &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;$ARGUMENTS&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="bef7"&gt;/fix-issue 142 high&lt;/span&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;span id="a367"&gt;&lt;span&gt;# In your .md file:&lt;/span&gt;&lt;br&gt;Fix issue #$1 with priority $2. Check the issue description&lt;br&gt;and implement the necessary changes.&lt;/span&gt;&lt;/pre&gt;
&lt;p id="0143"&gt;One more thing worth knowing: Anthropic merged the old &lt;code&gt;.claude/commands/&lt;/code&gt; system with a newer &lt;code&gt;.claude/skills/&lt;/code&gt; system. Your existing command files still work fine, but new ones should go in &lt;code&gt;.claude/skills/&lt;/code&gt; if you want access to newer features like shell-injected context and agent configuration. For everything in this article, either location works.&lt;/p&gt;
&lt;p id="972e"&gt;Setup time is genuinely four minutes. Create the folder, add your first &lt;code&gt;.md&lt;/code&gt; file, type &lt;code&gt;/&lt;/code&gt; in Claude Code and watch it appear in autocomplete. Once you see it, you can't unsee it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="9172"&gt;&lt;strong&gt;Now the actual commands.&lt;/strong&gt;&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;

&lt;p id="f21b"&gt;&lt;strong&gt;Commands 1–3: The daily drivers&lt;/strong&gt;&lt;/p&gt;

&lt;p id="a713"&gt;These three you’ll use every single session. If you only set up three commands from this entire article, make it these.&lt;/p&gt;


&lt;/blockquote&gt;
&lt;h2 id="95da"&gt;Command 1: &lt;code&gt;/init&lt;/code&gt; give Claude a memory&lt;/h2&gt;
&lt;p id="2956"&gt;Every time you start a fresh Claude Code session, it has zero context about your project. No idea what your build command is, what conventions you follow, what folders matter. So it scans. It reads. It figures things out from scratch, eating your tokens and your time before you’ve even asked it anything useful.&lt;/p&gt;
&lt;p id="ca76"&gt;&lt;code&gt;/init&lt;/code&gt; fixes this. Run it once in a new project and it generates a &lt;code&gt;CLAUDE.md&lt;/code&gt; file a persistent memory file that Claude reads at the start of every session automatically.&lt;/p&gt;
&lt;pre&gt;&lt;span id="18ee"&gt;&lt;span&gt;# CLAUDE.md&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span&gt;## Project overview&lt;/span&gt;&lt;br&gt;FastAPI inference service with scikit-learn models and Alembic migrations.&lt;br&gt;&lt;span&gt;## Key commands&lt;/span&gt;&lt;br&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;&lt;code&gt;make dev&lt;/code&gt;&lt;/span&gt; - start local server&lt;br&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;&lt;code&gt;make test&lt;/code&gt;&lt;/span&gt; - run pytest suite&lt;br&gt;&lt;span&gt;-&lt;/span&gt; &lt;span&gt;&lt;code&gt;make lint&lt;/code&gt;&lt;/span&gt; - black + ruff&lt;br&gt;&lt;span&gt;## Conventions&lt;/span&gt;&lt;br&gt;&lt;span&gt;-&lt;/span&gt; All endpoints return typed Pydantic models&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Never commit directly to main&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Migrations live in /alembic/versions&lt;/span&gt;&lt;/pre&gt;
&lt;p id="7ce2"&gt;You can edit this file manually after generation and you should. The more specific it is, the less Claude wastes time figuring out context that you already know. Think of it as your project’s onboarding doc, except the new hire is an AI that reads at the speed of light and forgets everything between sessions.&lt;/p&gt;
&lt;p id="ec08"&gt;&lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://shipyard.build/blog/claude-code-cheat-sheet/" rel="noopener ugc nofollow noreferrer"&gt;CLAUDE.md explained Shipyard&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="5768"&gt;Command 2: &lt;code&gt;/compact&lt;/code&gt; save your context window before it saves you&lt;/h2&gt;
&lt;p id="ec8f"&gt;Here’s a thing that happens to everyone once: your session gets long, Claude starts referencing variables that don’t exist, suggests refactors you already did an hour ago, and generally acts like it’s been awake for 30 hours. That’s context window overflow. The conversation history got too big and it’s degrading the outputs.&lt;/p&gt;
&lt;p id="0ab5"&gt;&lt;code&gt;/compact&lt;/code&gt; compresses the conversation history keeps the important stuff, strips the redundant back-and-forth, and gives you clean working memory again.&lt;/p&gt;
&lt;pre&gt;&lt;span id="0729"&gt;&lt;span&gt;# Basic compact&lt;/span&gt;&lt;br&gt;/compact&lt;br&gt;&lt;br&gt;&lt;span&gt;# Compact with focus instruction - keeps specific context&lt;/span&gt;&lt;br&gt;/compact focus on the auth refactor, ignore the CSS discussion&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5269"&gt;The trick is using it &lt;em&gt;proactively&lt;/em&gt;, not reactively. Don’t wait for Claude to start hallucinating. Run it before switching to a new phase of work within the same session. I run it roughly every 45 minutes on long coding days.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="4211"&gt;Command 3: &lt;code&gt;/review&lt;/code&gt; automated PR review that actually finds bugs&lt;/h2&gt;
&lt;p id="5c69"&gt;&lt;code&gt;/review&lt;/code&gt; triggers a code review of your recent changes. Out of the box it's useful, but out of the box it's also extremely chatty it'll write a paragraph about your variable naming while missing the actual logic error two lines below.&lt;/p&gt;
&lt;p id="0073"&gt;The fix is a &lt;code&gt;claude-code-review.yml&lt;/code&gt; override file that tightens the prompt. This is the one the &lt;a href="https://www.builder.io/blog/claude-code" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Builder.io team&lt;/strong&gt;&lt;/a&gt; landed on after their default review was "way too verbose":&lt;/p&gt;
&lt;pre&gt;&lt;span id="38ea"&gt;&lt;span&gt;# .claude/claude-code-review.yml&lt;/span&gt;&lt;br&gt;&lt;span&gt;direct_prompt:&lt;/span&gt; &lt;span&gt;|&lt;br&gt;  Review this pull request and look for bugs and security issues only.&lt;br&gt;  Do not comment on style, naming, or formatting.&lt;br&gt;  Be concise. Only report what you actually find.&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="8b01"&gt;With that config in place, &lt;code&gt;/review&lt;/code&gt; stops being a noisy linter and starts behaving like a senior dev who skips the lectures and just spots real problems. It catches logic errors. It catches security gaps. It doesn't care that you named a variable &lt;code&gt;data&lt;/code&gt;.&lt;/p&gt;
&lt;p id="479e"&gt;If you’re on a team and your PR volume is climbing because of AI-assisted development and it probably is this command pays for itself inside a week.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;

&lt;p id="5ee1"&gt;&lt;strong&gt;Commands 4–6: The workflow multipliers&lt;/strong&gt;&lt;/p&gt;

&lt;p id="5f32"&gt;These are the commands that kill the grunt work. The stuff that isn’t hard, just annoyingly repetitive and repetitive tasks are exactly where prompt drift quietly destroys your consistency.&lt;/p&gt;


&lt;/blockquote&gt;
&lt;h2 id="d209"&gt;Command 4: &lt;code&gt;/commit-msg&lt;/code&gt; never write a commit message again&lt;/h2&gt;
&lt;p id="f2fd"&gt;Show of hands: how many of your last ten commit messages were some variation of “fix bug”, “update”, or “wip”? Yeah. We’ve all been there. And if you’re on a team that actually enforces conventional commits, you’ve probably spent a non-trivial portion of your career typing &lt;code&gt;feat(auth):&lt;/code&gt; before a sentence you half-thought through.&lt;/p&gt;
&lt;p id="2e7b"&gt;&lt;strong&gt;Create this file at &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;.claude/commands/commit-msg.md&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="7178"&gt;---&lt;br&gt;description: Generate a conventional commit message from staged changes&lt;br&gt;&lt;span&gt;allowed-tools: Bash(git diff --staged), Bash(git log --oneline -5)&lt;br&gt;---&lt;/span&gt;&lt;br&gt;Read the staged diff and the last 5 commits for context.&lt;br&gt;Generate a conventional commit message following this format:&lt;br&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;short summary&amp;gt;&lt;br&gt;Types: feat, fix, refactor, test, docs, chore, perf&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Summary must be under 72 characters&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Use imperative mood ("add" not "added")&lt;br&gt;&lt;span&gt;-&lt;/span&gt; If breaking change, append ! after type/scope&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Output only the commit message, nothing else&lt;/span&gt;&lt;/pre&gt;
&lt;p id="bb58"&gt;Now run &lt;code&gt;/commit-msg&lt;/code&gt; after staging your files and Claude reads the actual diff, understands the scope, and writes the message. Not from memory. Not from vibe. From the code.&lt;/p&gt;
&lt;p id="0924"&gt;My commit history went from “fix stuff” to something a PM could read and a changelog generator could parse. It felt like cheating for about two days and then it just felt normal.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="12d9"&gt;Command 5: &lt;code&gt;/scaffold&lt;/code&gt; boilerplate at warp speed&lt;/h2&gt;
&lt;p id="f93b"&gt;Every project has patterns. A React component always needs the same basic structure. An Express route always gets the same middleware stack. An API handler always needs the same error boundary. You know this. You’ve written it four hundred times.&lt;/p&gt;
&lt;p id="7283"&gt;&lt;code&gt;/scaffold&lt;/code&gt; is a custom command where you encode &lt;em&gt;your&lt;/em&gt; project's patterns not some generic template from the internet, but the actual conventions your codebase uses and generate new files that already fit.&lt;/p&gt;
&lt;p id="2291"&gt;&lt;strong&gt;Create &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;.claude/commands/scaffold.md&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="4b77"&gt;---&lt;br&gt;description: Scaffold a new component or module following project conventions&lt;br&gt;allowed-tools: Read, Write, Bash(ls src/)&lt;br&gt;&lt;span&gt;argument-hint: [type] [name] [--tests] [--stories]&lt;br&gt;---&lt;/span&gt;&lt;br&gt;Scaffold a new $1 named $2 following the existing patterns in src/.&lt;br&gt;&lt;br&gt;Before generating, read 2-3 existing $1 files to match conventions exactly.&lt;br&gt;If --tests flag is passed, generate a test file alongside it.&lt;br&gt;If --stories flag is passed, generate a Storybook story file.&lt;br&gt;&lt;br&gt;Match: naming conventions, import style, export pattern, folder structure.&lt;br&gt;Do not invent patterns that don't exist in the codebase.&lt;br&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="3d8e"&gt;&lt;strong&gt;Then fire it:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="73a1"&gt;/scaffold react-component UserProfile --tests --stories&lt;br&gt;/scaffold express-route /api/payments --tests&lt;br&gt;/scaffold db-model Subscription&lt;/span&gt;&lt;/pre&gt;
&lt;p id="b2cf"&gt;The key line is &lt;em&gt;“read 2–3 existing files to match conventions exactly.”&lt;/em&gt; That’s what makes this different from every other scaffold tool it learns from your actual codebase instead of generating code that looks slightly off and needs four manual edits before it fits.&lt;/p&gt;
&lt;p id="26cd"&gt;&lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://github.com/alirezarezvani/claude-code-tresor/tree/main/commands" rel="noopener ugc nofollow noreferrer"&gt;claude-code-tresor commands library&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1050%2F1%2AqtgHkEopIPQIYptVBL3llQ.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="b369"&gt;Command 6: &lt;code&gt;/test-gen&lt;/code&gt; from 28% coverage to something respectable&lt;/h2&gt;
&lt;p id="7ef1"&gt;Test coverage is one of those things every dev agrees matters and approximately nobody enjoys actually writing. The work is tedious, the feedback loop is slow, and there’s always something more interesting to build. So coverage sits at 28% for six months while everyone silently agrees not to mention it in standup.&lt;/p&gt;
&lt;p id="ce50"&gt;&lt;strong&gt;Create &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;.claude/commands/test-gen.md&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="774e"&gt;---&lt;br&gt;description: Generate comprehensive tests for a given file&lt;br&gt;allowed-tools: Read, Write, Bash(cat package.json)&lt;br&gt;&lt;span&gt;argument-hint: [filepath] [--framework] [--coverage-gaps]&lt;br&gt;---&lt;/span&gt;&lt;br&gt;&lt;br&gt;Generate tests for $1.&lt;br&gt;First, read the file and identify: exported functions, edge cases,&lt;br&gt;error paths, and any integration points.&lt;br&gt;Check package.json to confirm the test framework in use.&lt;br&gt;If --coverage-gaps is passed, run existing tests first and only&lt;br&gt;generate tests for uncovered paths.&lt;br&gt;Follow the existing test style in the &lt;span&gt;&lt;strong&gt;tests&lt;/strong&gt;&lt;/span&gt; folder.&lt;br&gt;Do not test implementation details - test behavior.&lt;/span&gt;&lt;/pre&gt;
&lt;p id="9eeb"&gt;&lt;strong&gt;Usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="581a"&gt;/test-gen src/utils/auth.ts --framework vitest&lt;br&gt;/test-gen src/api/payments.ts --coverage-gaps&lt;/span&gt;&lt;/pre&gt;
&lt;p id="d97c"&gt;The &lt;code&gt;--coverage-gaps&lt;/code&gt; flag is the real unlock. Instead of generating redundant tests for already-covered paths, it reads what exists and fills the holes. Pointed this at a legacy utils folder on a Friday afternoon. Coverage went from 28% to 71% before I closed my laptop. First time I've ever looked forward to writing tests because I wasn't really writing them.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;blockquote&gt;

&lt;p id="5ab6"&gt;Commands 7–9: The power moves&lt;/p&gt;

&lt;p id="fa1c"&gt;These three are the ones most devs never discover because they’re not in any getting-started guide. They’re also the ones I’d miss most if they disappeared tomorrow.&lt;/p&gt;


&lt;/blockquote&gt;
&lt;h2 id="cc6c"&gt;Command 7: &lt;code&gt;/security-check&lt;/code&gt; your own security scanner, always on&lt;/h2&gt;
&lt;p id="c81f"&gt;Security audits are one of those things teams either do religiously or completely skip depending on whether someone got burned recently. The problem with the “wait until it hurts” approach is obvious in retrospect and invisible in the moment.&lt;/p&gt;
&lt;p id="9c94"&gt;This command runs a focused security scan on your codebase every time you want it no external service, no SaaS subscription, no waiting for a quarterly audit.&lt;/p&gt;
&lt;p id="b029"&gt;&lt;strong&gt;Create &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;.claude/commands/security-check.md&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="209c"&gt;---&lt;br&gt;description: Scan codebase for common security vulnerabilities&lt;br&gt;allowed-tools: Read, Grep, Glob&lt;br&gt;&lt;span&gt;model: claude-opus-4-6&lt;br&gt;---&lt;/span&gt;&lt;br&gt;&lt;br&gt;Analyze the codebase at $1 (or current directory if no argument) for:&lt;br&gt;&lt;br&gt;&lt;span&gt;-&lt;/span&gt; SQL injection risks (raw queries, unparameterized inputs)&lt;br&gt;&lt;span&gt;-&lt;/span&gt; XSS vulnerabilities (unsanitized user input in rendered output)&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Exposed credentials (hardcoded keys, secrets in non-.env files)&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Insecure configurations (debug mode in prod, open CORS, weak auth)&lt;br&gt;&lt;span&gt;-&lt;/span&gt; Dependency confusion risks in package manifests&lt;br&gt;&lt;br&gt;For each issue found:&lt;br&gt;&lt;span&gt;1.&lt;/span&gt; Show the exact file and line number&lt;br&gt;&lt;span&gt;2.&lt;/span&gt; Explain why it's a risk&lt;br&gt;&lt;span&gt;3.&lt;/span&gt; Suggest the specific fix&lt;br&gt;&lt;br&gt;If nothing is found, say so clearly. Do not invent issues.&lt;/span&gt;&lt;/pre&gt;
&lt;p id="7f50"&gt;&lt;strong&gt;Usage:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="c3b0"&gt;/security-check src/api&lt;br&gt;/security-check .   &lt;span&gt;# scans everything&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="ee80"&gt;Two things worth noting. First, the &lt;code&gt;model: claude-opus-4-6&lt;/code&gt; pin — this is the one command where you want the smartest model on the job, not the fastest. Security reasoning is exactly the kind of nuanced, multi-step analysis where Opus earns its cost. Second, the "do not invent issues" line at the bottom is load-bearing. Without it, Claude occasionally hallucinates vulnerabilities in clean code. Guardrails in prompts matter.&lt;/p&gt;
&lt;p id="1047"&gt;&lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://platform.claude.com/docs/en/agent-sdk/slash-commands" rel="noopener ugc nofollow noreferrer"&gt;Anthropic slash commands docs&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="b7b7"&gt;Command 8: &lt;code&gt;/rewind&lt;/code&gt; the undo button for your entire session&lt;/h2&gt;
&lt;p id="c925"&gt;This one’s a built-in, not a custom command and it might be the most underrated thing in Claude Code.&lt;/p&gt;
&lt;p id="52d6"&gt;Here’s the scenario: Claude misunderstands your intent, goes down a rabbit hole, and refactors three files you didn’t ask it to touch. You come back from grabbing coffee and the codebase looks like it had an argument with itself. Now you have to figure out what changed, manually revert files, and re-explain your original intent from scratch while Claude carries the wrong context from the previous exchange.&lt;/p&gt;
&lt;p id="a70b"&gt;&lt;code&gt;/rewind&lt;/code&gt; handles all of that in one command. It reverts both the conversation history &lt;em&gt;and&lt;/em&gt; the file changes back to a previous checkpoint implicit checkpoints that Claude creates as you work.&lt;/p&gt;
&lt;pre&gt;&lt;span id="1002"&gt;/rewind&lt;br&gt;&lt;span&gt;# Opens interactive checkpoint picker — select how far back to go&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="67be"&gt;&lt;strong&gt;The workflow that actually works:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="0635"&gt;&lt;span&gt;# 1. Ask Claude to attempt something risky&lt;/span&gt;&lt;br&gt;&lt;span&gt;# 2. Review with /diff before accepting&lt;/span&gt;&lt;br&gt;&lt;span&gt;# 3. If it went sideways → /rewind back to before the attempt&lt;/span&gt;&lt;br&gt;&lt;span&gt;# 4. Rephrase the prompt with more constraints&lt;/span&gt;&lt;br&gt;&lt;span&gt;# 5. Try again&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="5664"&gt;I used to waste 30–45 minutes manually reverting bad sessions and re-establishing context. Now it’s a 10-second &lt;code&gt;/rewind&lt;/code&gt; and a better prompt. The number of times this has saved a late-night session from becoming a full rollback is embarrassing to admit.&lt;/p&gt;
&lt;p id="4e62"&gt;&lt;strong&gt;Reference:&lt;/strong&gt; &lt;a href="https://batsov.com/articles/2026/03/11/essential-claude-code-skills-and-commands/" rel="noopener ugc nofollow noreferrer"&gt;batsov.com essential Claude Code commands&lt;/a&gt;&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="446" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1050%2F1%2AdahO6v000HWhS8mOEZ6i0A.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="83b1"&gt;Command 9: &lt;code&gt;/diff&lt;/code&gt; catch mistakes before they compound&lt;/h2&gt;
&lt;p id="298f"&gt;Also built-in. Also criminally underused.&lt;/p&gt;
&lt;p id="03ca"&gt;&lt;code&gt;/diff&lt;/code&gt; opens an interactive viewer showing every single file change Claude has made in the current session. Not just the last change everything, across all files, since the session started.&lt;/p&gt;
&lt;pre&gt;&lt;span id="5406"&gt;/diff&lt;br&gt;&lt;span&gt;# Interactive viewer: scroll through all changes, file by file&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="24ae"&gt;The reason this matters is compounding. Claude makes a small wrong assumption in step 2, builds on it in step 4, builds on &lt;em&gt;that&lt;/em&gt; in step 7, and by step 9 you have a structurally coherent but fundamentally misaligned set of changes that are painful to unpick. Each individual edit looked fine in isolation. The sum of them drifted from what you actually wanted.&lt;/p&gt;
&lt;p id="3af2"&gt;The practice that fixed this for me: run &lt;code&gt;/diff&lt;/code&gt; before every &lt;code&gt;/commit-msg&lt;/code&gt;. Every time, without exception. It takes 90 seconds and it's caught at least a dozen "wait, why did it touch that file" moments that would've shipped as bugs.&lt;/p&gt;
&lt;p id="a7ac"&gt;Think of it as a mandatory code review with yourself before anything reaches Git. Not glamorous. Genuinely important.&lt;/p&gt;
&lt;pre&gt;&lt;span id="12d7"&gt;&lt;span&gt;# The rhythm that works:&lt;/span&gt;&lt;br&gt;/test-gen src/feature.ts&lt;br&gt;&lt;span&gt;# ... Claude writes tests ...&lt;/span&gt;&lt;br&gt;/diff                    &lt;span&gt;# review everything before committing&lt;/span&gt;&lt;br&gt;/commit-msg              &lt;span&gt;# generate message from clean, verified diff&lt;/span&gt;&lt;br&gt;git commit -m &lt;span&gt;"&lt;span&gt;$(pbpaste)&lt;/span&gt;"&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="293a"&gt;Command 10: &lt;code&gt;/preflight&lt;/code&gt; the team play that ended merge conflicts&lt;/h2&gt;
&lt;p id="da57"&gt;This one’s a custom command but it thinks like a system. It’s not doing one thing it’s doing everything your team agreed should happen before code touches the remote branch, automated into a single slash command that runs the same way every time for every person on the team.&lt;/p&gt;
&lt;p id="86db"&gt;&lt;strong&gt;Create &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;.claude/commands/preflight.md&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt;:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;span id="fd02"&gt;---&lt;br&gt;description: Full pre-commit check — lint, types, debug artifacts, code smells&lt;br&gt;&lt;span&gt;allowed-tools: Bash(git &lt;span&gt;&lt;em&gt;), Bash(grep *&lt;/em&gt;&lt;/span&gt;), Bash(make &lt;span&gt;), Read, Glob&lt;br&gt;---&lt;br&gt;&lt;br&gt;Run a full preflight check before committing. In order:&lt;br&gt;1. Scan staged files for debug artifacts:&lt;br&gt;   - console.log, debugger, TODO, FIXME, hardcoded localhost URLs&lt;br&gt;   - Report file + line number for each hit&lt;br&gt;&lt;br&gt;2. Run linter: &lt;code&gt;make lint&lt;/code&gt;&lt;br&gt;   - If it fails, show only the errors, not the full output&lt;br&gt;&lt;br&gt;3. Run type check: &lt;code&gt;make typecheck&lt;/code&gt;&lt;br&gt;   - If it fails, show the first 10 errors only&lt;br&gt;&lt;br&gt;4. Check for obvious code smells:&lt;br&gt;   - Functions over 50 lines&lt;br&gt;   - Deeply nested conditionals (4+ levels)&lt;br&gt;   - Duplicate logic blocks over 10 lines&lt;br&gt;&lt;br&gt;5. Final summary:&lt;br&gt;   - ✅ if all checks pass - safe to commit&lt;br&gt;   - ❌ list of what failed and where - do not auto-fix anything&lt;br&gt;&lt;br&gt;Do not make any changes. Report only.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;
&lt;p id="bc80"&gt;The last two lines are critical. This command is an observer, not an actor. It tells you what’s wrong, you decide what to fix. Giving an automated pre-commit command write access to your staged files is how you end up with a codebase that fixed itself into a broken state.&lt;/p&gt;
&lt;p id="9b7d"&gt;The team impact is the real story here. When this command lives in &lt;code&gt;.claude/commands/&lt;/code&gt; and gets committed to Git, every developer on the team runs the exact same preflight check. Same lint rules, same type check, same artifact scan, same code smell thresholds. No more "it passed on my machine." No more forgotten &lt;code&gt;console.log&lt;/code&gt; that makes it to staging. No more merge conflicts because two people formatted the same file differently.&lt;/p&gt;
&lt;p id="0251"&gt;Zero merge conflicts last sprint. First time in 18 months. I’m not saying &lt;code&gt;/preflight&lt;/code&gt; is entirely responsible for that but it's not not responsible either.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="49a7"&gt;Wrapping up: your prompts are code now, treat them that way&lt;/h2&gt;
&lt;p id="421b"&gt;Here’s the thing that took me too long to fully internalize: the way you use Claude Code is a skill that compounds. Every command you write, version-control, and share with your team is a permanent improvement to how your entire team ships. It doesn’t reset when you close the terminal. It doesn’t drift when someone new joins. It just works, every time, the same way.&lt;/p&gt;
&lt;p id="0ecb"&gt;Most devs are still using Claude Code like a chatbot with a terminal window. That’s not a criticism it’s just where the default usage pattern lands. But there’s a real gap between “using Claude Code” and “building a command library that encodes your team’s best practices into reusable, version-controlled workflows.” The teams on the right side of that gap are shipping noticeably faster, with fewer regressions, and with less tribal knowledge living inside individual developers’ heads.&lt;/p&gt;
&lt;p id="e002"&gt;The slightly uncomfortable take: in a year or two, your &lt;code&gt;.claude/commands/&lt;/code&gt; folder is going to matter as much as your dotfiles. It's going to be one of the first things you clone when you set up a new machine. Devs will share command libraries the way they share vim configs today obsessively, opinionatedly, and with strong feelings about the right way to do it.&lt;/p&gt;
&lt;p id="b0a3"&gt;So start building yours. Start small one &lt;code&gt;/preflight&lt;/code&gt;, one &lt;code&gt;/commit-msg&lt;/code&gt;, one &lt;code&gt;/review&lt;/code&gt; override. Get it into Git. Share it with your team. See what happens to your sprint velocity.&lt;/p&gt;
&lt;p id="8a0a"&gt;What’s your most-used custom command? Drop it in the comments I’m genuinely curious what workflows people are encoding that I haven’t thought of yet.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="d8f6"&gt;Helpful resources&lt;/h2&gt;
&lt;ul&gt;

&lt;li id="68e6"&gt;&lt;a href="https://platform.claude.com/docs/en/agent-sdk/slash-commands" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Claude Code custom slash commands&lt;/strong&gt; official docs&lt;/a&gt;&lt;/li&gt;

&lt;li id="4dcd"&gt;&lt;a href="https://github.com/hesreallyhim/awesome-claude-code" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;awesome-claude-code&lt;/strong&gt; curated skills, hooks, and commands&lt;/a&gt;&lt;/li&gt;

&lt;li id="f2bb"&gt;&lt;a href="https://github.com/wshobson/commands" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;wshobson/commands &lt;/strong&gt;57 production-ready commands&lt;/a&gt;&lt;/li&gt;

&lt;li id="f4d7"&gt;&lt;a href="https://github.com/alirezarezvani/claude-code-tresor/tree/main/commands" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;claude-code-tresor &lt;/strong&gt;open source command library&lt;/a&gt;&lt;/li&gt;

&lt;li id="de7e"&gt;&lt;a href="https://www.builder.io/blog/claude-code" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Builder.io&lt;/strong&gt; how I use Claude Code (deep dive)&lt;/a&gt;&lt;/li&gt;

&lt;li id="8775"&gt;&lt;a href="https://batsov.com/articles/2026/03/11/essential-claude-code-skills-and-commands/" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;batsov.com&lt;/strong&gt; essential Claude Code skills and commands&lt;/a&gt;&lt;/li&gt;

&lt;li id="93ae"&gt;&lt;a href="https://shipyard.build/blog/claude-code-cheat-sheet/" rel="noopener ugc nofollow noreferrer"&gt;Shipyard Claude Code cheatsheet&lt;/a&gt;&lt;/li&gt;

&lt;li id="aea1"&gt;&lt;a href="https://blog.dailydoseofds.com/p/10-must-use-slash-commands-in-claude" rel="noopener ugc nofollow noreferrer"&gt;&lt;strong&gt;Daily Dose of DS&lt;/strong&gt; 10 must-use slash commands&lt;/a&gt;&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>claude</category>
      <category>coding</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why GitHub feels like it’s dying in the AI era?</title>
      <dc:creator>&lt;devtips/&gt;</dc:creator>
      <pubDate>Thu, 16 Apr 2026 07:01:04 +0000</pubDate>
      <link>https://forem.com/dev_tips/why-github-feels-like-its-dying-in-the-ai-era-519o</link>
      <guid>https://forem.com/dev_tips/why-github-feels-like-its-dying-in-the-ai-era-519o</guid>
      <description>&lt;p&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h1 id="ff3f"&gt;&lt;/h1&gt;
&lt;h2 id="258b"&gt;&lt;strong&gt;We didn’t stop coding. we just stopped exploring. AI quietly replaced one of the most important dev habits and nobody’s really talking about it.&lt;/strong&gt;&lt;/h2&gt;
&lt;span&gt;&lt;/span&gt;&lt;p id="52b8"&gt;“&lt;strong&gt;GitHub is dying&lt;/strong&gt;” sounds like one of those takes you see, roll your eyes at, and keep scrolling.&lt;/p&gt;
&lt;p id="54f8"&gt;Like yeah, sure. Next you’ll tell me tabs are better than spaces and start a war in the comments.&lt;/p&gt;
&lt;p id="7f74"&gt;But here’s the uncomfortable part: something &lt;em&gt;is&lt;/em&gt; changing and it’s not subtle.&lt;/p&gt;
&lt;p id="b281"&gt;A few months ago, if I hit a weird bug, my flow was basically muscle memory:&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="a4cf"&gt;Google → random blog → GitHub issue → Stack Overflow → copy something questionable → pray → ship.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="de5f"&gt;&lt;strong&gt;Now?&lt;/strong&gt;&lt;/p&gt;
&lt;p id="35f6"&gt;I just open ChatGPT or let GitHub Copilot autocomplete half my brain, and I’m done before my coffee cools.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="4763"&gt;No digging through repos.&lt;br&gt;No reading long issue threads.&lt;br&gt;No “why does this even work?” moment.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="e4a8"&gt;Just… solution acquired.&lt;/p&gt;
&lt;p id="a605"&gt;And I realized something slightly weird:&lt;/p&gt;
&lt;p id="56f6"&gt;I hadn’t &lt;em&gt;browsed GitHub&lt;/em&gt; in days.&lt;/p&gt;
&lt;p id="e040"&gt;Not for discovery. Not for learning. Not even out of curiosity.&lt;/p&gt;
&lt;p id="a81d"&gt;That felt off. Because GitHub wasn’t just a tool it used to be a place. You’d wander into random repos at midnight, read code like a detective, star things you didn’t fully understand, and somehow come out smarter.&lt;/p&gt;
&lt;p id="b24b"&gt;Now it’s starting to feel like background infrastructure. Like electricity. Still essential… but invisible.&lt;/p&gt;
&lt;h3 id="d411"&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/h3&gt;
&lt;ul&gt;

&lt;li id="5f33"&gt;GitHub isn’t actually dying&lt;/li&gt;

&lt;li id="9214"&gt;But the way developers &lt;em&gt;use&lt;/em&gt; it is shifting fast&lt;/li&gt;

&lt;li id="a142"&gt;AI tools are replacing exploration with instant answers&lt;/li&gt;

&lt;li id="ff11"&gt;Open-source culture might be taking a quiet hit because of it&lt;/li&gt;

&lt;li id="98c5"&gt;And most of us didn’t even notice it happening&lt;/li&gt;

&lt;/ul&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="b9ac"&gt;Github was never just code hosting&lt;/h2&gt;
&lt;p id="2f2f"&gt;It’s easy to think of GitHub as “that place where my repos live.”&lt;br&gt;Like a cloud Dropbox… but with commit guilt and merge conflicts.&lt;br&gt;But if you’ve been around long enough, you know that’s not even close to the full story.&lt;br&gt;GitHub wasn’t just infrastructure it was a &lt;em&gt;hangout spot&lt;/em&gt; for developers.&lt;br&gt;When I first started pushing code, I didn’t care about CI/CD pipelines or clean commit history. I cared about one thing:&lt;/p&gt;
&lt;p id="2c21"&gt;&lt;strong&gt;Someone starring my repo.&lt;/strong&gt;&lt;/p&gt;
&lt;p id="c2ac"&gt;That tiny star felt illegal. Like… why is a random person on the internet validating my spaghetti code?&lt;/p&gt;
&lt;p id="cb68"&gt;And then you start exploring.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="1d05"&gt;You click on who starred your repo → check their profile → see what &lt;em&gt;they’re&lt;/em&gt; building → fall into a rabbit hole of projects → suddenly it’s been an hour and you’ve learned three new ways to structure an API without opening a single tutorial.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="2de8"&gt;That loop? That was GitHub’s real power.&lt;/p&gt;
&lt;p id="af2c"&gt;&lt;strong&gt;GitHub quietly became this weird hybrid of:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="534f"&gt;A portfolio (your green squares were basically your XP bar)&lt;/li&gt;

&lt;li id="606a"&gt;A social network (stars, forks, followers)&lt;/li&gt;

&lt;li id="8612"&gt;A discovery engine (trending page was dev TikTok before TikTok)&lt;/li&gt;

&lt;li id="a394"&gt;And yeah… a code host&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="8c61"&gt;It wasn’t just where code &lt;em&gt;lived&lt;/em&gt; it was where developers &lt;em&gt;learned by osmosis&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="5a16"&gt;You didn’t just read docs.&lt;br&gt;You read real code. Messy code. Genius code. Code that made you question your life choices.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="9cef"&gt;And that mattered.&lt;/p&gt;
&lt;p id="f613"&gt;&lt;strong&gt;Because there’s a huge difference between:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="ecc8"&gt;Reading a tutorial on how authentication works&lt;/li&gt;

&lt;li id="2d04"&gt;Vs digging through a production repo and seeing how someone &lt;em&gt;actually&lt;/em&gt; handled auth, edge cases, and all the weird hacks they didn’t put in the blog post&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="373c"&gt;One teaches you theory.&lt;br&gt;The other teaches you survival.&lt;/p&gt;
&lt;p id="8a29"&gt;There was also this unspoken culture around it.&lt;/p&gt;
&lt;p id="0d22"&gt;You’d find a repo with 20k stars and think, “okay this is probably legit.”&lt;br&gt;You’d read through issues like you were reading a story arc bugs, drama, fixes, debates.&lt;br&gt;Sometimes the comments were more educational than entire courses.&lt;/p&gt;
&lt;p id="f743"&gt;And contributing? That was a whole character arc.&lt;/p&gt;
&lt;p id="e359"&gt;Your first PR felt like applying for a job. You reread your own code ten times, convinced you broke the internet. &lt;br&gt;&lt;strong&gt;Then a maintainer leaves a comment like:&lt;/strong&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="8c59"&gt;“Hey, can you rename this variable?”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="000e"&gt;&lt;strong&gt;And you’re like… that’s it? I’m in??&lt;/strong&gt;&lt;/p&gt;
&lt;p id="828b"&gt;Even the UI nudged you into curiosity.&lt;/p&gt;
&lt;p id="29c2"&gt;You’d land on the trending page and see things like React or TensorFlow blowing up, and suddenly you’re opening repos you had no business understanding yet.&lt;/p&gt;
&lt;p id="00ca"&gt;But you tried anyway.&lt;br&gt;And that’s kind of the point.&lt;br&gt;GitHub trained an entire generation of developers to &lt;em&gt;explore first, understand later&lt;/em&gt;.&lt;/p&gt;
&lt;p id="49a0"&gt;&lt;strong&gt;The wild part?&lt;/strong&gt;&lt;/p&gt;
&lt;p id="e937"&gt;None of this was explicitly designed as “learning.”&lt;br&gt;There was no curriculum. No structured path. No roadmap.&lt;/p&gt;
&lt;p id="7835"&gt;&lt;strong&gt;Just:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="6122"&gt;Curiosity&lt;/li&gt;

&lt;li id="a82c"&gt;Public code&lt;/li&gt;

&lt;li id="79e8"&gt;And a little bit of ego&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="e893"&gt;And somehow, that combination worked ridiculously wellso when people say “GitHub is just a tool,” it feels incomplete because for a lot of us, it was more like a playground.&lt;/p&gt;
&lt;p id="09bf"&gt;A messy, chaotic, sometimes broken playground… but one where you leveled up faster than you realized and that’s exactly why what’s happening now feels different not because GitHub is going away but because fewer people are &lt;em&gt;wandering around it anymore&lt;/em&gt;.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="3f97"&gt;The shift: from searching code to generating code&lt;/h2&gt;
&lt;p id="ef37"&gt;There’s a subtle shift happening in how we solve problems as developers and it didn’t arrive with a big announcement. It just… slipped into our workflow.&lt;/p&gt;
&lt;p id="b5fc"&gt;The old loop was almost ritualistic. You’d hit an error, copy it, throw it into Google, open three tabs you didn’t trust, land on Stack Overflow, scroll past a passive-aggressive comment, and eventually find a thread that &lt;em&gt;almost&lt;/em&gt; matched your issue. Then you’d end up on a GitHub issue page from 2019, where someone had the exact same problem and a fix that may or may not break everything else.&lt;/p&gt;
&lt;p id="b1d0"&gt;It was messy. Slow. Sometimes painful. But it forced you to &lt;em&gt;think&lt;/em&gt;. You read context. You compared solutions. You accidentally learned things you weren’t even looking for.&lt;/p&gt;
&lt;p id="ed74"&gt;Now the loop looks completely different.&lt;/p&gt;
&lt;p id="e614"&gt;You open ChatGPT or rely on GitHub Copilot, paste your error, maybe add a sentence like “this is happening in my React auth flow,” and boom — you get a clean, confident answer. Sometimes even multiple options. No tabs. No digging. No wandering.&lt;/p&gt;
&lt;p id="c8e7"&gt;Just output.&lt;/p&gt;
&lt;p id="1f51"&gt;And yeah… it works.&lt;/p&gt;
&lt;p id="0227"&gt;That’s the part that makes this shift hard to argue against. It’s not worse it’s &lt;em&gt;better&lt;/em&gt; in terms of speed. You go from “I need to understand this problem” to “I need this problem gone” in seconds.&lt;/p&gt;
&lt;p id="ae6c"&gt;But something quietly disappears in that transition.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="22d4"&gt;You stop exploring.&lt;br&gt;You stop reading other people’s code.&lt;br&gt;You stop seeing how different developers approached the same problem.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="8da5"&gt;Instead of navigating through a messy ecosystem of ideas, you’re handed a synthesized answer that feels final even when it isn’t.&lt;/p&gt;
&lt;p id="26af"&gt;It’s like going from exploring an open-world game to using fast travel everywhere. You still reach the destination, but you miss everything in between.&lt;/p&gt;
&lt;p id="7cc5"&gt;I noticed this the other day while fixing a weird token refresh issue. A year ago, I would’ve gone deep read through auth libraries, checked how other repos handled edge cases, maybe even discovered a better pattern along the way. This time? I asked AI, got a solution, tweaked two lines, and moved on.&lt;/p&gt;
&lt;p id="b04e"&gt;It worked. I shipped.&lt;/p&gt;
&lt;p id="5de5"&gt;But if you asked me &lt;em&gt;why&lt;/em&gt; it worked… I’d probably give you a half-confident answer and change the subject.&lt;/p&gt;
&lt;p id="9f97"&gt;And that’s the trade-off we’re starting to normalize.&lt;/p&gt;
&lt;p id="4246"&gt;We’re compressing the learning process into a black box.&lt;/p&gt;
&lt;p id="348d"&gt;There’s also this weird side effect where code feels more disposable now. Before, you’d recognize snippets from popular repos or patterns you’d seen in the wild. Now, a lot of what we write (or generate) feels… anonymous. Like it could’ve come from anywhere.&lt;/p&gt;
&lt;p id="8e30"&gt;Because it kinda did.&lt;/p&gt;
&lt;p id="08b4"&gt;We’re no longer pulling from a specific repo or developer we’re pulling from a statistical blend of &lt;em&gt;everything&lt;/em&gt;.&lt;/p&gt;
&lt;p id="9284"&gt;And that changes your relationship with code in a subtle way.&lt;/p&gt;
&lt;p id="d19b"&gt;You’re not tracing ideas back to their source anymore. You’re not thinking, “oh this is how that library does it.” You’re thinking, “cool, this works.”&lt;/p&gt;
&lt;p id="a34a"&gt;Even boilerplate has changed. Instead of cloning starter repos or browsing templates on GitHub, you just ask for a setup. Need a Node API with JWT auth and rate limiting? Done. Need a Docker config? Generated. Need tests? Sure, why not.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="a275"&gt;No repo required.&lt;br&gt;No exploration needed.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="98a9"&gt;And again, this isn’t a complaint it’s just… different.&lt;/p&gt;
&lt;p id="29aa"&gt;Faster. Smoother. More efficient.&lt;/p&gt;
&lt;p id="add4"&gt;But also a little bit flatter.&lt;/p&gt;
&lt;p id="6a56"&gt;Because when everything becomes instant, you lose the friction that used to teach you something.&lt;/p&gt;
&lt;p id="cbd7"&gt;And that friction? That’s exactly what made GitHub such a valuable place to wander in the first place.&lt;/p&gt;
&lt;p id="099e"&gt;If the old internet taught us how to search, this new one is teaching us how to ask.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="91a3"&gt;The question is… what are we &lt;em&gt;not&lt;/em&gt; learning anymore?&lt;/p&gt;&lt;/blockquote&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="df8c"&gt;Open source is quietly losing oxygen&lt;/h2&gt;
&lt;p id="ac18"&gt;Here’s the part nobody really wants to say out loud.&lt;/p&gt;
&lt;p id="f3ae"&gt;AI didn’t just change how we write code it changed how we &lt;em&gt;participate&lt;/em&gt; in the ecosystem that taught us how to code in the first place.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="e116"&gt;Open source used to run on a simple loop: you use something → you break something → you dig into the repo → maybe you fix something → eventually you contribute.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="6b8f"&gt;That loop wasn’t perfect, but it worked.&lt;/p&gt;
&lt;p id="9784"&gt;Now? A lot of that loop just… stops halfway.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="4d72"&gt;You use something → it breaks → you ask ChatGPT → you get a fix → you move on.&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p id="225e"&gt;No issue opened.&lt;br&gt;No repo explored.&lt;br&gt;No contribution made.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="1eb3"&gt;Multiply that by millions of developers, and you start to feel the shift.&lt;/p&gt;
&lt;p id="cb0c"&gt;It’s like everyone still &lt;em&gt;consumes&lt;/em&gt; open source, but fewer people are actually &lt;em&gt;feeding it back&lt;/em&gt;.&lt;/p&gt;
&lt;p id="603b"&gt;There’s a weird analogy that keeps popping into my head.&lt;/p&gt;
&lt;p id="e659"&gt;Open source is starting to feel like Wikipedia if everyone read articles, but nobody edited them anymore.&lt;/p&gt;
&lt;p id="2c1c"&gt;The knowledge stays useful… for a while. But eventually, it gets stale. Maintainers burn out. Things stop evolving.&lt;/p&gt;
&lt;p id="755e"&gt;And we’re already seeing early signs of that.&lt;/p&gt;
&lt;p id="a945"&gt;Maintainers complaining about burnout.&lt;br&gt;Projects going quiet.&lt;br&gt;Issues sitting unanswered longer than they used to.&lt;/p&gt;
&lt;p id="fec2"&gt;Not because people don’t care but because fewer people are &lt;em&gt;showing up&lt;/em&gt;.&lt;/p&gt;
&lt;p id="01bb"&gt;And then there’s the other side of it.&lt;/p&gt;
&lt;p id="115e"&gt;When people &lt;em&gt;do&lt;/em&gt; contribute now, there’s a growing wave of low-effort, AI-generated pull requests.&lt;/p&gt;
&lt;p id="03d1"&gt;&lt;strong&gt;You’ll see things like:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;

&lt;li id="0316"&gt;Random refactors that don’t solve real problems&lt;/li&gt;

&lt;li id="242b"&gt;Overly verbose code that technically works but feels off&lt;/li&gt;

&lt;li id="36b6"&gt;PR descriptions that read like they were written by a polite robot&lt;/li&gt;

&lt;/ul&gt;
&lt;p id="975b"&gt;Maintainers have to filter through that noise, which honestly sounds exhausting.&lt;/p&gt;
&lt;p id="61b4"&gt;Imagine reviewing 20 PRs and half of them feel like someone just pasted from an AI without understanding the codebase.&lt;/p&gt;
&lt;p id="0efd"&gt;That’s not contribution that’s cleanup duty.&lt;/p&gt;
&lt;p id="3dd5"&gt;&lt;strong&gt;The uncomfortable truth is this:&lt;/strong&gt;&lt;/p&gt;
&lt;p id="14c6"&gt;AI is trained on open source, but it doesn’t contribute back to it.&lt;br&gt;It extracts value at scale, but the feedback loop the human part starts weakening.&lt;br&gt;And if that loop breaks long enough, the whole system slows down.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="d465"&gt;Less contribution → fewer improvements → weaker tools → more reliance on AI → even less contribution.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="ea9d"&gt;You can see where that spiral goes.&lt;/p&gt;
&lt;p id="7832"&gt;I felt this recently when I opened a repo I used to rely on. Issues were piling up, last meaningful commit was months ago, and the maintainer had a pinned note basically saying, “I don’t have time for this anymore.”&lt;/p&gt;
&lt;p id="72e3"&gt;That hit a bit harder than expected.&lt;/p&gt;
&lt;p id="dfd3"&gt;Because open source wasn’t just free tools it was people. Random devs on the internet deciding to build things and share them.&lt;/p&gt;
&lt;p id="ca60"&gt;And if fewer people feel the need to engage with that process, something important starts fading.&lt;/p&gt;
&lt;p id="be05"&gt;This doesn’t mean open source is “dying.”&lt;/p&gt;
&lt;p id="35fe"&gt;But it does mean the &lt;em&gt;energy&lt;/em&gt; around it is shifting.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="5a53"&gt;Less curiosity.&lt;br&gt;Less contribution.&lt;br&gt;More consumption.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="ba00"&gt;And if GitHub was the place where that energy used to live…&lt;/p&gt;
&lt;p id="9a32"&gt;then yeah, it makes sense why it feels a little quieter lately.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;img alt="" width="800" height="1033" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fresize%3Afit%3A1050%2F1%2Aqam69eNdpd6FUsBNCIVBuA.png"&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="cf1a"&gt;Github isn’t dying it’s being abstracted away&lt;/h2&gt;
&lt;p id="1076"&gt;Saying GitHub is “dying” is a bit dramatic.&lt;/p&gt;
&lt;p id="c994"&gt;What’s actually happening is simpler and honestly more interesting.&lt;/p&gt;
&lt;p id="dbb9"&gt;It’s becoming invisible.&lt;/p&gt;
&lt;p id="8702"&gt;Most of us still use GitHub every day… we just don’t &lt;em&gt;go to GitHub&lt;/em&gt; anymore.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="c0d9"&gt;You commit from your IDE.&lt;br&gt;You review PRs inside your editor.&lt;br&gt;You let GitHub Copilot suggest changes inline.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="0dc2"&gt;GitHub is still there it’s just sitting in the background like plumbing.&lt;/p&gt;
&lt;p id="9e8b"&gt;This is what happens to successful tools.&lt;/p&gt;
&lt;p id="b1a4"&gt;They stop being destinations and start becoming layers.&lt;/p&gt;
&lt;p id="ed20"&gt;Nobody “browses AWS” for fun. You just deploy stuff.&lt;br&gt;Nobody thinks about Git anymore you just commit and move on.&lt;/p&gt;
&lt;p id="2021"&gt;GitHub is heading in the same direction.&lt;/p&gt;
&lt;p id="9e76"&gt;Less website.&lt;br&gt;More infrastructure.&lt;/p&gt;
&lt;p id="d70e"&gt;I realized this when I checked my own workflow.&lt;/p&gt;
&lt;p id="35d1"&gt;There are days where I push commits, review code, even merge PRs… and never open github.com once.&lt;/p&gt;
&lt;p id="820b"&gt;Everything happens inside the editor.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="bfa4"&gt;No trending page.&lt;br&gt;No random repo exploration.&lt;br&gt;No falling into code rabbit holes.&lt;/p&gt;&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p id="7a57"&gt;Just task → commit → done.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="77ef"&gt;And that shift matters more than it looks.&lt;/p&gt;
&lt;p id="6ea5"&gt;Because when a platform becomes invisible, you stop &lt;em&gt;interacting&lt;/em&gt; with it beyond necessity.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="910b"&gt;You don’t wander.&lt;br&gt;You don’t discover.&lt;br&gt;You don’t get curious.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="c52a"&gt;You just use it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="a291"&gt;So yeah GitHub isn’t going anywhere.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="a791"&gt;But it’s slowly turning into something we &lt;em&gt;use without noticing&lt;/em&gt;.&lt;/p&gt;
&lt;p id="c0b0"&gt;Like electricity.&lt;/p&gt;
&lt;p id="cb9a"&gt;Always there.&lt;br&gt;Always critical.&lt;/p&gt;
&lt;p id="28b2"&gt;Just… no longer a place you hang out.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="ee38"&gt;What this means for developers&lt;/h2&gt;
&lt;p id="e371"&gt;This shift isn’t just about tools it changes what it means to be a developer.&lt;/p&gt;
&lt;p id="a73c"&gt;Before, a big part of leveling up was learning how to search. You got good at digging through GitHub, reading messy code, comparing approaches, and slowly building intuition.&lt;/p&gt;
&lt;p id="cd2e"&gt;Now the skill is different.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="2918"&gt;It’s less “can you find the answer?”&lt;br&gt;and more “can you judge if this answer is actually good?”&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="0c6f"&gt;Because tools like ChatGPT and GitHub Copilot will give you something almost instantly.&lt;/p&gt;
&lt;p id="ba55"&gt;The problem is… it’s not always the right thing.&lt;/p&gt;
&lt;p id="baca"&gt;There’s a growing gap forming between two types of devs.&lt;/p&gt;
&lt;p id="47a9"&gt;The first one ships fast. Uses AI for everything. Gets things working quickly.&lt;/p&gt;
&lt;p id="1732"&gt;The second one still digs deeper. Understands why things work. Knows when AI is hallucinating or suggesting something slightly dangerous.&lt;/p&gt;
&lt;p id="478c"&gt;Both can build.&lt;/p&gt;
&lt;p id="9b4c"&gt;But only one can debug when things go sideways.&lt;/p&gt;
&lt;p id="bcd7"&gt;I’ve felt this myself reviewing AI-generated code.&lt;/p&gt;
&lt;p id="4595"&gt;On the surface, it looks clean. Functions named well. Comments make sense. Tests even pass.&lt;/p&gt;
&lt;p id="1cb3"&gt;But something feels… off.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="2b40"&gt;Maybe it’s over-engineered.&lt;br&gt;Maybe it ignores an edge case.&lt;br&gt;Maybe it solves the problem but in a way that won’t scale.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="f0f3"&gt;You can’t always see the issue immediately you feel it from experience and that’s the part AI can’t shortcut for you.&lt;/p&gt;
&lt;p id="4f80"&gt;There’s also a risk for newer developers.&lt;br&gt;If you skip the “wander around GitHub and break things” phase, you miss out on building that intuition.&lt;/p&gt;
&lt;p id="5400"&gt;You become really good at prompting… but not as good at reasoning.&lt;/p&gt;
&lt;p id="0ee1"&gt;Like using auto-aim in a shooter you’ll hit targets, but your raw aim never improves.&lt;/p&gt;
&lt;p id="a7a9"&gt;That said, it’s not all doom.&lt;/p&gt;
&lt;p id="b2d0"&gt;This is also kind of a superpower era.&lt;/p&gt;
&lt;p id="d642"&gt;Solo devs can build faster than ever.&lt;br&gt;Side projects that used to take weeks now take days.&lt;br&gt;You can prototype ideas without getting stuck in boilerplate hell.&lt;/p&gt;
&lt;p id="0c46"&gt;That’s huge.&lt;/p&gt;
&lt;p id="0c87"&gt;So the move isn’t to reject AI or go back to manually digging through repos like it’s 2015.&lt;/p&gt;
&lt;p id="2b7f"&gt;It’s to balance it.&lt;/p&gt;
&lt;p id="6e5d"&gt;Use AI to move fast.&lt;br&gt;But still take time to understand what you’re shipping.&lt;br&gt;Still read real code sometimes.&lt;br&gt;Still open a random repo and explore like you used to.&lt;/p&gt;
&lt;p id="c0de"&gt;Because in the end, the devs who win won’t be the ones who rely on AI the most.&lt;/p&gt;
&lt;p id="d983"&gt;It’ll be the ones who can work with it without losing their ability to think.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="622c"&gt;Conclusion github isn’t dead, but the culture might be&lt;/h2&gt;
&lt;p id="7e90"&gt;So no GitHub isn’t dying.&lt;br&gt;If anything, it’s more critical than ever.&lt;/p&gt;
&lt;p id="9d0b"&gt;But the way we &lt;em&gt;interact&lt;/em&gt; with it? That’s changing fast.&lt;/p&gt;
&lt;p id="56c1"&gt;We used to explore.&lt;br&gt;Now we execute.&lt;/p&gt;
&lt;p id="b131"&gt;We used to learn by wandering through repos.&lt;br&gt;Now we get answers handed to us.&lt;/p&gt;
&lt;p id="9dd1"&gt;And yeah, that makes us faster but maybe a little less curious.&lt;/p&gt;
&lt;p id="8a07"&gt;The real loss isn’t the platform.&lt;/p&gt;
&lt;p id="e31c"&gt;It’s the culture around it.&lt;/p&gt;
&lt;blockquote&gt;&lt;p id="e83b"&gt;The late-night repo deep dives.&lt;br&gt;The random discoveries.&lt;br&gt;The “wait… this is how they did it?” moments.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p id="2ee2"&gt;That’s the stuff that quietly made people better developers.&lt;/p&gt;
&lt;p id="b841"&gt;Moving forward, it’s probably not about choosing sides.&lt;/p&gt;
&lt;p id="9708"&gt;Not “AI vs GitHub.”&lt;/p&gt;
&lt;p id="d012"&gt;It’s about not letting speed replace understanding completely.&lt;/p&gt;
&lt;p id="620b"&gt;Because the devs who stay curious even in an AI-first world are the ones who’ll actually stand out.&lt;/p&gt;
&lt;p id="a4d9"&gt;We didn’t lose GitHub.&lt;br&gt;We just stopped visiting it.&lt;/p&gt;
&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;h2 id="8fcb"&gt;Helpful resources&lt;/h2&gt;
&lt;p id="f8ac"&gt;If you want to explore this shift yourself (or just go down a few good rabbit holes again), here are some solid starting points:&lt;/p&gt;
&lt;ul&gt;

&lt;li id="4f5b"&gt;

&lt;strong&gt;GitHub Docs&lt;/strong&gt; &lt;a href="https://docs.github.com" rel="noopener ugc nofollow noreferrer"&gt;&lt;/a&gt;&lt;a href="https://docs.github.com" rel="noopener noreferrer"&gt;https://docs.github.com&lt;/a&gt;
&lt;/li&gt;

&lt;li id="2aca"&gt;

&lt;strong&gt;GitHub Copilot Docs&lt;/strong&gt; &lt;a href="https://docs.github.com/en/copilot" rel="noopener ugc nofollow noreferrer"&gt;&lt;/a&gt;&lt;a href="https://docs.github.com/en/copilot" rel="noopener noreferrer"&gt;https://docs.github.com/en/copilot&lt;/a&gt;
&lt;/li&gt;

&lt;li id="0eea"&gt;

&lt;strong&gt;OpenAI API&lt;/strong&gt; &lt;a href="https://platform.openai.com/docs" rel="noopener ugc nofollow noreferrer"&gt;&lt;/a&gt;&lt;a href="https://platform.openai.com/docs" rel="noopener noreferrer"&gt;https://platform.openai.com/docs&lt;/a&gt;
&lt;/li&gt;

&lt;li id="3871"&gt;

&lt;strong&gt;Visual Studio Code GitHub integration &lt;/strong&gt;&lt;a href="https://code.visualstudio.com/docs/sourcecontrol/overview" rel="noopener ugc nofollow noreferrer"&gt;&lt;/a&gt;&lt;a href="https://code.visualstudio.com/docs/sourcecontrol/overview" rel="noopener noreferrer"&gt;https://code.visualstudio.com/docs/sourcecontrol/overview&lt;/a&gt;
&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>git</category>
      <category>github</category>
    </item>
  </channel>
</rss>
