<?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>DEV Community: max</title>
    <description>The latest articles on DEV Community by max (@maxtendies).</description>
    <link>https://dev.to/maxtendies</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%2F3739199%2F58571add-a1fc-4810-9c9a-55689e783815.png</url>
      <title>DEV Community: max</title>
      <link>https://dev.to/maxtendies</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maxtendies"/>
    <language>en</language>
    <item>
      <title>The Debugging Checklist I Run Before Asking Anyone for Help</title>
      <dc:creator>max</dc:creator>
      <pubDate>Mon, 02 Feb 2026 08:56:59 +0000</pubDate>
      <link>https://dev.to/maxtendies/the-debugging-checklist-i-run-before-asking-anyone-for-help-6e5</link>
      <guid>https://dev.to/maxtendies/the-debugging-checklist-i-run-before-asking-anyone-for-help-6e5</guid>
      <description>&lt;p&gt;Last Tuesday I spent 40 minutes convinced my API was broken. Requests were timing out, the logs looked fine, and I was drafting a message to a friend who knows the codebase when I realized the bug was in my .env file. I had a trailing space after a database URL. A trailing space.&lt;/p&gt;

&lt;p&gt;Forty minutes. For a space character.&lt;/p&gt;

&lt;p&gt;I should have caught it in under five. I have a process for this. I just skipped it because I was tired and figured this one would be obvious. It was not obvious. They never are when you skip the process.&lt;/p&gt;

&lt;p&gt;So here is the actual checklist I run through before I message a coworker, open a Stack Overflow tab, or do anything that involves another human looking at my dumb mistake. This is not theory. This is the stuff that lives in my hands at this point.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1: Confirm the Bug Exists Where You Think It Does
&lt;/h2&gt;

&lt;p&gt;Most debugging advice is useless because it assumes you already know where the bug is. That is a massive assumption. Half the time, the bug is not even in the file you are staring at.&lt;/p&gt;

&lt;p&gt;Before doing anything else, prove to yourself that the system is actually broken in the way you think it is. Not "it feels broken" or "the output looks wrong." Run a request manually. Check the actual response body. Look at the actual database row. Hit the actual endpoint with curl.&lt;/p&gt;

&lt;p&gt;I cannot tell you how many times I have been debugging the wrong thing entirely. The frontend is showing stale cached data and the API is fine. The test is failing because the test setup is wrong, not because the code is wrong. The deploy did not actually go out yet.&lt;/p&gt;

&lt;p&gt;Stop assuming. Verify.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Check What Changed
&lt;/h2&gt;

&lt;p&gt;This is the highest-value question in all of debugging and most people skip it.&lt;/p&gt;

&lt;p&gt;Was this working before? When did it stop? What changed between then and now?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
git diff HEAD~5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look at your recent commits. Check if someone else merged something. Look at your environment variables, your config files, your dependencies. Did a package update? Did someone change a shared resource? Did your database get migrated?&lt;/p&gt;

&lt;p&gt;If you can narrow it down to "it broke after this specific change," you have just eliminated 95% of the codebase from your investigation. That is not a small thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Read the Actual Error Message
&lt;/h2&gt;

&lt;p&gt;I know this sounds condescending. I promise it is not.&lt;/p&gt;

&lt;p&gt;I have watched developers, including myself, glance at an error message, get the gist of it, and then go off debugging based on their interpretation of what the error probably means. Then they spend an hour chasing the wrong thing because the error message was quite specific and they just did not read the whole thing.&lt;/p&gt;

&lt;p&gt;Read it. The whole thing. Including the stack trace. Including the part after the first line. Including the "caused by" section at the bottom that everyone scrolls past.&lt;/p&gt;

&lt;p&gt;If there is a line number, go to that line. If there is a file path, open that file. If there is an error code, look up that specific code. Not the category. The specific code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Reproduce It Reliably
&lt;/h2&gt;

&lt;p&gt;If you cannot make the bug happen on command, you are not ready to debug it. You are guessing.&lt;/p&gt;

&lt;p&gt;Strip away everything you can. If the bug happens in a web app, can you trigger it from a unit test? From a curl command? From a REPL? The fewer moving parts between you and the bug, the faster you will find it.&lt;/p&gt;

&lt;p&gt;Here is the thing though. If you cannot reproduce it, that IS the clue. Intermittent bugs are almost always one of three things: race conditions, state leaking between tests, or something environment-specific. That narrows your search enormously.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5: Add Logging at the Boundaries
&lt;/h2&gt;

&lt;p&gt;Not everywhere. At the boundaries.&lt;/p&gt;

&lt;p&gt;Log what goes into the function and what comes out. Log what the database query returns. Log what the external API sends back. You are trying to find the exact point where the data goes from correct to incorrect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;INPUT:  &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;do_the_thing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;OUTPUT: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yeah, print debugging. I know. I have a debugger. I know how to use breakpoints. But nine times out of ten, a few strategic print statements find the bug faster than stepping through code because they let me see the data flow across multiple function calls at once instead of one frame at a time.&lt;/p&gt;

&lt;p&gt;I am not saying debuggers are bad. I am saying the fastest path to the bug is usually the one with the least setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6: Question Your Assumptions
&lt;/h2&gt;

&lt;p&gt;This is the hard one.&lt;/p&gt;

&lt;p&gt;By the time you have been staring at a bug for 20 minutes, you have built a mental model of what is happening. That mental model is probably wrong in at least one way, and that wrong assumption is probably why you have not found the bug yet.&lt;/p&gt;

&lt;p&gt;Force yourself to question the things you "know":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are you sure this code is actually being executed? Add a log and check.&lt;/li&gt;
&lt;li&gt;Are you sure that variable contains what you think it does? Print it.&lt;/li&gt;
&lt;li&gt;Are you sure the database has the data you expect? Query it directly.&lt;/li&gt;
&lt;li&gt;Are you sure you are hitting the right server? Check the URL. Check it again.&lt;/li&gt;
&lt;li&gt;Are you sure the config file you are editing is the one the app is reading?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one has bitten me at least four times. Editing a .env file in one directory while the process is reading from a completely different one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7: Isolate With Binary Search
&lt;/h2&gt;

&lt;p&gt;If you have a big chunk of code and you know the bug is in there somewhere, do not read every line hoping to spot it. That works sometimes. It is slow every time.&lt;/p&gt;

&lt;p&gt;Comment out half the code. Does the bug still happen? If yes, it is in the other half. If no, it is in the half you commented out. Repeat.&lt;/p&gt;

&lt;p&gt;This works for more than just code. It works for config files, for SQL queries, for CSS stylesheets, for request payloads. Anything where you have a blob of stuff and the problem is somewhere inside it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 8: Search for the Exact Error
&lt;/h2&gt;

&lt;p&gt;I do this later in the process than most people recommend, and here is why: if you Google the error message before understanding your own code, you will find a Stack Overflow answer that looks relevant, apply the fix blindly, and either introduce a new bug or mask the original one.&lt;/p&gt;

&lt;p&gt;But once you have done steps 1 through 7 and you actually understand the shape of the problem? Then searching is incredibly effective because you can evaluate whether a solution actually applies to your situation.&lt;/p&gt;

&lt;p&gt;Put the error in quotes. Include the specific library or framework version. Filter to results from the last year or two. Old answers for old versions will ruin your afternoon.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 9: Rubber Duck It (Seriously)
&lt;/h2&gt;

&lt;p&gt;I used to think this was silly advice from people who had never debugged anything serious. I was wrong.&lt;/p&gt;

&lt;p&gt;The act of explaining the problem out loud, or writing it out as if you are going to post it somewhere, forces you to organize your understanding. And the gaps in your understanding become obvious when you try to articulate them.&lt;/p&gt;

&lt;p&gt;I write the Stack Overflow question. Title, description, what I have tried, what I expected, what actually happened. I would estimate 30% of the time, I find the answer while writing the question. The other 70%, I at least have a well-structured question ready to post.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 10: Let a Second Brain Look at It
&lt;/h2&gt;

&lt;p&gt;I was pretty skeptical about AI coding tools for a while. Thought it was all hype. Then sometime in mid-2024 I pasted a module into Claude on a whim, mostly to prove to a friend it would not find anything useful. It caught a race condition I had been missing for weeks. A subtle one too, where two async handlers were writing to the same object and the outcome depended on which one resolved first. I had been staring at that code for days.&lt;/p&gt;

&lt;p&gt;Now this is a regular step in my process. Not the first step. Not a replacement for actually understanding the problem. But once I have done the work above and I am genuinely stuck, I will paste in the relevant code with a specific description of what is going wrong. Not "review this code" but "this function returns the wrong total when called with concurrent requests and I suspect it is a state mutation issue, here is my evidence."&lt;/p&gt;

&lt;p&gt;The key word there is specific. Vague questions get vague answers, from humans and AI alike.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Whole List, Condensed
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Confirm the bug is real and is where you think it is&lt;/li&gt;
&lt;li&gt;Check what changed recently&lt;/li&gt;
&lt;li&gt;Read the complete error message&lt;/li&gt;
&lt;li&gt;Reproduce it reliably&lt;/li&gt;
&lt;li&gt;Log at the boundaries&lt;/li&gt;
&lt;li&gt;Question your assumptions&lt;/li&gt;
&lt;li&gt;Binary search to isolate&lt;/li&gt;
&lt;li&gt;Search with context&lt;/li&gt;
&lt;li&gt;Rubber duck it&lt;/li&gt;
&lt;li&gt;Let a second brain look&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I do not always do all ten. Sometimes step 2 solves it instantly. Sometimes I jump straight to step 5 because I have a hunch. The point is not rigid adherence. The point is that when I am stuck, I have a concrete next action instead of staring at the screen hoping for insight.&lt;/p&gt;

&lt;p&gt;The developers I respect most are not the ones who never get stuck. They are the ones who get unstuck fast. And they get unstuck fast because they have a process, not because they are smarter.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I ended up documenting a lot of my debugging prompts and workflow patterns for AI-assisted development in a &lt;a href="https://maxtendies.gumroad.com/l/claude-code-prompt-pack" rel="noopener noreferrer"&gt;Claude Code Prompt Pack&lt;/a&gt; I put together for myself. It includes the exact prompt structures I use for bug hunting, code review, and the kind of targeted analysis that actually surfaces real issues. Figured I might as well share it since I was already maintaining it anyway.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>debugging</category>
      <category>programming</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What I Wish Someone Told Me Before I Went Freelance</title>
      <dc:creator>max</dc:creator>
      <pubDate>Sun, 01 Feb 2026 09:02:04 +0000</pubDate>
      <link>https://dev.to/maxtendies/what-i-wish-someone-told-me-before-i-went-freelance-5d3k</link>
      <guid>https://dev.to/maxtendies/what-i-wish-someone-told-me-before-i-went-freelance-5d3k</guid>
      <description>&lt;p&gt;I was sitting on the floor of my apartment in April of my first year freelancing, staring at TurboTax telling me I owed the IRS $14,000 I did not have. Not "I'd prefer not to spend this" money. I literally did not have it. I had been freelancing for about nine months at that point, cashing checks and spending like I was still getting a W-2 with taxes already pulled out.&lt;/p&gt;

&lt;p&gt;That moment rewired my entire understanding of what it means to work for yourself.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Money Is Not What You Think It Is
&lt;/h2&gt;

&lt;p&gt;My first freelance client paid me $40/hr for a React app. I remember texting a friend about it like I had won something. Forty bucks an hour. That is over 80 grand a year if I bill full-time. I was making about $95k at my salaried job, but $40/hr felt different. It felt like more.&lt;/p&gt;

&lt;p&gt;It was not more.&lt;/p&gt;

&lt;p&gt;Self-employment tax alone eats about 15% off the top before you even get to income tax. Then there is health insurance, which went from "something my employer handles" to "$480/month for a plan with a deductible so high it barely qualifies as insurance." No 401k match. No paid vacation. No sick days. When I finally did the real math, I was making less per hour than my salaried job.&lt;/p&gt;

&lt;p&gt;Nobody told me to do that math before I quit.&lt;/p&gt;




&lt;h2&gt;
  
  
  You Will Get Screwed on an Invoice
&lt;/h2&gt;

&lt;p&gt;This is not a maybe. This is a when.&lt;/p&gt;

&lt;p&gt;I did some work for a small agency early on. Good communication during the project. Friendly people. Delivered everything on time. Sent a $6,800 invoice and then just... nothing. For four months. Emails got vague. "We're processing it." "Accounting is backed up." "Let me check on that."&lt;/p&gt;

&lt;p&gt;I had no contract. No late fees clause. No kill fee. Nothing.&lt;/p&gt;

&lt;p&gt;I got the money eventually. Took a borderline-aggressive email and some very uncomfortable phone calls. But that experience taught me something that all the freelancing advice blogs skip over: the absence of a contract does not just mean you lack legal protection. It means the client does not take the engagement seriously. A contract is not a legal document. It is a psychological one. It tells the client "this person runs a real business" instead of "this person will probably let it slide."&lt;/p&gt;

&lt;p&gt;Get a contract. Put late fees in it. Put a kill fee in it. Put a clause that says work stops if payment is 15 days late. I do not care if it feels awkward. Being broke for four months because you were too polite feels worse.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Freedom Thing Is Real but Weird
&lt;/h2&gt;

&lt;p&gt;Yeah, you can work from anywhere. You can take a Tuesday off. You can work at 2am if that is when your brain decides to cooperate.&lt;/p&gt;

&lt;p&gt;But nobody warns you about the guilt.&lt;/p&gt;

&lt;p&gt;Every hour you are not working, you are not billing. Every afternoon you spend at the park with your dog, some part of your brain is calculating how much money you just did not make. Salaried workers do not have this problem. They get paid whether they stare at their screen productively or spend 45 minutes in a meeting that should have been a Slack message. Freelancers internalize every unbilled hour as a personal failing.&lt;/p&gt;

&lt;p&gt;It took me almost two years to stop feeling guilty about not working on a Saturday. Two years. And I still catch myself doing the math sometimes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nobody Prepares You for the Admin Work
&lt;/h2&gt;

&lt;p&gt;I became a freelance developer because I wanted to write code. What I actually spend my time on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code: maybe 60%&lt;/li&gt;
&lt;li&gt;Chasing invoices, doing bookkeeping, tracking expenses: 15%&lt;/li&gt;
&lt;li&gt;Writing proposals, scoping projects, handling client communication: 15%&lt;/li&gt;
&lt;li&gt;Figuring out taxes, quarterly estimates, insurance, retirement accounts: 10%&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last 40% is the job nobody tells you about. And if you are bad at it, the 60% does not matter because you will either go broke or burn out managing chaos.&lt;/p&gt;

&lt;p&gt;After the $14k tax situation, I built myself a spreadsheet system to track income, set aside tax money automatically, and flag when quarterly payments were due. My accountant actually complimented it, which might be the saddest professional achievement of my life. But it kept me from ever being surprised by a tax bill again.&lt;/p&gt;




&lt;h2&gt;
  
  
  Most Freelancing Advice Is Terrible
&lt;/h2&gt;

&lt;p&gt;I need to say this because it drove me nuts when I was starting out.&lt;/p&gt;

&lt;p&gt;The internet is full of freelancing content that boils down to "charge what you're worth" and "just raise your rates." As if the problem is that freelancers have not considered making more money. Thanks. Revolutionary stuff.&lt;/p&gt;

&lt;p&gt;Here is what actually matters and nobody wants to talk about: the first year sucks. You will underprice yourself. You will take bad clients because you need the money. You will say yes to projects you should say no to. You will work weekends not because you are passionate but because you quoted 40 hours and the project is going to take 70.&lt;/p&gt;

&lt;p&gt;That is normal. It is not a failure of mindset or hustle or whatever other nonsense the LinkedIn crowd is selling. It is the learning curve of running a business when nobody taught you how to run a business.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Part Where I Tell You to Do It Anyway
&lt;/h2&gt;

&lt;p&gt;Four years in, I bill at roughly three times what that first client paid me. I pick my clients. I take time off without asking permission. Last month I worked four-day weeks the entire month and nobody noticed or cared.&lt;/p&gt;

&lt;p&gt;But I want to be honest: the first 18 months were genuinely hard. Not "character building" hard in the way people romanticize. Hard in the "I might need to go get a real job again" way. The tax disaster. The unpaid invoice. A month where I had zero clients and zero income and started refreshing LinkedIn job listings out of panic.&lt;/p&gt;

&lt;p&gt;Would I do it again? Every time. But I would do it differently. I would start with a contract template, a real accounting system, and six months of savings instead of three. I would charge more from day one even if it meant fewer clients. I would set aside 30% of every payment for taxes before I touched a cent of it.&lt;/p&gt;

&lt;p&gt;Most importantly, I would stop reading advice from people who have been freelancing for six months and already have a course about it.&lt;/p&gt;




&lt;p&gt;I put together a &lt;a href="https://maxtendies.gumroad.com/l/freelance-developer-business-kit" rel="noopener noreferrer"&gt;Freelance Developer Business Kit&lt;/a&gt; a while back. It started as the spreadsheet system I built after the IRS situation and grew into contract templates, rate calculators, and the quarterly tax workflow I actually use. I go back and forth on whether to even mention it in posts like this, but it would have saved me a lot of pain in year one and I figure someone reading this is about to make the same mistakes I did.&lt;/p&gt;

</description>
      <category>freelancing</category>
      <category>career</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>14 Linux Command-Line Tricks That Made Me Mass-Delete My Bash Aliases</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 21:59:55 +0000</pubDate>
      <link>https://dev.to/maxtendies/14-linux-command-line-tricks-that-made-me-mass-delete-my-bash-aliases-575l</link>
      <guid>https://dev.to/maxtendies/14-linux-command-line-tricks-that-made-me-mass-delete-my-bash-aliases-575l</guid>
      <description>&lt;p&gt;I have been using the terminal daily for over a decade. I thought I knew my way around. Then a coworker shared a one-liner that did in 3 seconds what my janky shell script did in 40 lines. I mass-deleted half my aliases that week.&lt;/p&gt;

&lt;p&gt;Here are the tricks that did it. Some of these are genuinely obscure. Others are hiding in plain sight in tools you already use every day.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Process Substitution (The One That Changes Everything)
&lt;/h2&gt;

&lt;p&gt;You probably know pipes. You might know redirects. But process substitution is the thing most developers never learn, and it is absurdly powerful.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file1.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file2.txt&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;(...)&lt;/code&gt; syntax runs a command and presents its output as if it were a file. This means you can use it anywhere a filename is expected. Want to diff two remote files without downloading them first?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.example.com/v1/config&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://api.example.com/v2/config&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or compare two branches of a JSON config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git show main:config.json | jq &lt;span class="nt"&gt;-S&lt;/span&gt; .&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git show dev:config.json | jq &lt;span class="nt"&gt;-S&lt;/span&gt; .&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you internalize this, you stop creating temp files forever.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. xargs -P: Instant Parallelism
&lt;/h2&gt;

&lt;p&gt;Most people know &lt;code&gt;xargs&lt;/code&gt;. Almost nobody uses the &lt;code&gt;-P&lt;/code&gt; flag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'*.png'&lt;/span&gt; | xargs &lt;span class="nt"&gt;-P&lt;/span&gt; 8 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; optipng &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;-P 8&lt;/code&gt; runs 8 processes in parallel. Compressing 200 images? This turns a 10-minute job into a 1-minute job. It works with anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;urls.txt | xargs &lt;span class="nt"&gt;-P&lt;/span&gt; 10 &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; curl &lt;span class="nt"&gt;-sO&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ten parallel downloads, no scripting, no background jobs to manage.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. The Curly Brace Expansion Nobody Uses Fully
&lt;/h2&gt;

&lt;p&gt;You have probably done &lt;code&gt;mkdir -p src/{components,utils,hooks}&lt;/code&gt;. But brace expansion goes deeper than most realize.&lt;/p&gt;

&lt;p&gt;Rename a file without typing the path twice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; /long/annoying/path/config.json&lt;span class="o"&gt;{&lt;/span&gt;,.bak&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That expands to &lt;code&gt;mv /long/annoying/path/config.json /long/annoying/path/config.json.bak&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Sequence expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;01..12&lt;span class="o"&gt;}&lt;/span&gt;       &lt;span class="c"&gt;# 01 02 03 04 05 06 07 08 09 10 11 12&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;a..z..3&lt;span class="o"&gt;}&lt;/span&gt;      &lt;span class="c"&gt;# a d g j m p s v y&lt;/span&gt;
&lt;span class="nb"&gt;mkdir &lt;/span&gt;day-&lt;span class="o"&gt;{&lt;/span&gt;01..31&lt;span class="o"&gt;}&lt;/span&gt;  &lt;span class="c"&gt;# 31 directories in one shot&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nested expansion for combinatorial generation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;api,web&lt;span class="o"&gt;}&lt;/span&gt;-&lt;span class="o"&gt;{&lt;/span&gt;dev,staging,prod&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;# api-dev api-staging api-prod web-dev web-staging web-prod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. Column Selection With cut and awk That Actually Makes Sense
&lt;/h2&gt;

&lt;p&gt;Stop writing janky loops to parse columnar output.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get just PIDs of processes matching 'node'&lt;/span&gt;
ps aux | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/node/ {print $2}'&lt;/span&gt;

&lt;span class="c"&gt;# Grab the 3rd field from a CSV&lt;/span&gt;
&lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-f3&lt;/span&gt; data.csv

&lt;span class="c"&gt;# Get last field regardless of how many fields exist&lt;/span&gt;
&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $NF}'&lt;/span&gt; file.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;$NF&lt;/code&gt; trick is the one that saves you the most time. NF means "number of fields" so &lt;code&gt;$NF&lt;/code&gt; is always the last column. No more counting fields.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. The &lt;code&gt;!!&lt;/code&gt; and &lt;code&gt;!$&lt;/code&gt; History Shortcuts
&lt;/h2&gt;

&lt;p&gt;You forgot sudo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;something
&lt;span class="c"&gt;# Permission denied&lt;/span&gt;
&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;!!&lt;/code&gt; repeats the entire last command. But &lt;code&gt;!$&lt;/code&gt; is even more useful -- it grabs the last argument of the previous command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /some/deeply/nested/new/directory
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No retyping, no reaching for the mouse to copy-paste a path.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. The &lt;code&gt;column&lt;/code&gt; Command: Instant Pretty Tables
&lt;/h2&gt;

&lt;p&gt;You have data that looks like garbage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"name,age,city&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;alice,30,nyc&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;bob,25,sf"&lt;/span&gt; | column &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;name   age  city
alice  30   nyc
bob    25   sf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pipe any CSV, TSV, or delimited output through &lt;code&gt;column -t&lt;/code&gt; and it becomes readable. I use this daily for quick data inspection.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. &lt;code&gt;comm&lt;/code&gt;: The Set Operations Tool You Never Knew Existed
&lt;/h2&gt;

&lt;p&gt;Want lines that are only in file A, only in file B, or in both? &lt;code&gt;comm&lt;/code&gt; does set operations on sorted files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;comm&lt;/span&gt; &lt;span class="nt"&gt;-23&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file1.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file2.txt&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Only in file1&lt;/span&gt;
&lt;span class="nb"&gt;comm&lt;/span&gt; &lt;span class="nt"&gt;-13&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file1.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file2.txt&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# Only in file2&lt;/span&gt;
&lt;span class="nb"&gt;comm&lt;/span&gt; &lt;span class="nt"&gt;-12&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file1.txt&lt;span class="o"&gt;)&lt;/span&gt; &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sort &lt;/span&gt;file2.txt&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# In both files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the process substitution from trick #1. These compose beautifully together. The flags suppress columns: &lt;code&gt;-2&lt;/code&gt; suppresses "only in file2", &lt;code&gt;-3&lt;/code&gt; suppresses "in both". So &lt;code&gt;-23&lt;/code&gt; gives you "only in file1."&lt;/p&gt;

&lt;p&gt;This is how I diff environment variable lists between servers, compare package versions, and find missing translations.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. &lt;code&gt;tar&lt;/code&gt; Can Stream Over SSH
&lt;/h2&gt;

&lt;p&gt;Forget &lt;code&gt;scp&lt;/code&gt; for directories. This is faster and does not require disk space on either side for intermediary files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;tar &lt;/span&gt;czf - /local/directory | ssh user@remote &lt;span class="s1"&gt;'tar xzf - -C /remote/path'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-&lt;/code&gt; means stdin/stdout. You are creating a tar stream, piping it over SSH, and extracting on the other side in real time. No temp files, no zipping then transferring then unzipping.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. &lt;code&gt;tee&lt;/code&gt; For When You Need Both
&lt;/h2&gt;

&lt;p&gt;Want to see output AND save it to a file?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;long-running-build 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee &lt;/span&gt;build.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the real power move is using &lt;code&gt;tee&lt;/code&gt; mid-pipeline to debug what is flowing through:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;access.log | &lt;span class="nb"&gt;tee&lt;/span&gt; /dev/stderr | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'ERROR'&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prints the full stream to stderr (so you see it) while the pipeline continues to count errors. Invaluable when a pipeline is giving unexpected results and you need to see the intermediate data.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. &lt;code&gt;grep -c&lt;/code&gt; and &lt;code&gt;sort | uniq -c | sort -rn&lt;/code&gt;: The Analysis Pipeline
&lt;/h2&gt;

&lt;p&gt;This three-command pipeline is the most underrated data analysis tool on any system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; access.log | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt; | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-20&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you the top 20 most frequent values in column 1. Change the &lt;code&gt;awk&lt;/code&gt; field number and you can instantly answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What IPs hit us most? (&lt;code&gt;$1&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;What endpoints are hottest? (&lt;code&gt;$7&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;What status codes dominate? (&lt;code&gt;$9&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can answer questions that would take an Elasticsearch query or a Python script in under 5 seconds with this pattern.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. &lt;code&gt;watch&lt;/code&gt;: Auto-Refresh Any Command
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;watch &lt;span class="nt"&gt;-n&lt;/span&gt; 2 &lt;span class="s1"&gt;'kubectl get pods | grep -v Running'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This re-runs the command every 2 seconds and shows you the output. Add &lt;code&gt;-d&lt;/code&gt; to highlight what changed between refreshes. I use this constantly for monitoring deployments, watching disk space during big operations, and tracking build progress.&lt;/p&gt;




&lt;h2&gt;
  
  
  12. Redirect stderr and stdout Independently
&lt;/h2&gt;

&lt;p&gt;Most devs know &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;. Fewer know you can send them to completely different places:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; output.log 2&amp;gt; errors.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or discard errors while keeping output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find / &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'config.yml'&lt;/span&gt; 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or swap them entirely (stdout becomes stderr and vice versa):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;command &lt;/span&gt;3&amp;gt;&amp;amp;1 1&amp;gt;&amp;amp;2 2&amp;gt;&amp;amp;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last one uses file descriptor 3 as a temp swap variable. It is the kind of thing that looks unhinged until you need it, and then you really need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  13. &lt;code&gt;rsync --dry-run&lt;/code&gt;: Preview Before You Wreck
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rsync &lt;span class="nt"&gt;-avhn&lt;/span&gt; &lt;span class="nb"&gt;source&lt;/span&gt;/ destination/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-n&lt;/code&gt; flag is &lt;code&gt;--dry-run&lt;/code&gt;. It shows you exactly what rsync would do without doing it. I run this before every significant file sync. The one time it saves you from overwriting production assets pays for the habit permanently.&lt;/p&gt;




&lt;h2&gt;
  
  
  14. Trap: Clean Up After Yourself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;tmpfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;trap&lt;/span&gt; &lt;span class="s1"&gt;'rm -f "$tmpfile"'&lt;/span&gt; EXIT

&lt;span class="c"&gt;# Use $tmpfile however you want&lt;/span&gt;
&lt;span class="c"&gt;# It gets cleaned up automatically when the script exits&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;trap&lt;/code&gt; builtin runs a command when the shell receives a signal. &lt;code&gt;EXIT&lt;/code&gt; fires when the script ends for any reason -- normal exit, error, even Ctrl+C. No more orphaned temp files cluttering &lt;code&gt;/tmp&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;Notice that none of these tricks require installing anything. They are all built into standard Linux distributions and have been for decades. The command line is not a relic. It is a composable toolkit where every piece connects to every other piece through text streams.&lt;/p&gt;

&lt;p&gt;The developers who move fastest in the terminal are not typing faster. They are combining these primitives in ways that eliminate entire categories of manual work.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I keep a set of cheatsheets pinned next to my monitor for exactly these kinds of tricks -- organized by tool, with flags, examples, and real-world patterns I actually use. I put together a &lt;a href="https://maxtendies.gumroad.com/l/developer-cli-cheatsheet-bundle" rel="noopener noreferrer"&gt;CLI Cheatsheet Bundle&lt;/a&gt; that covers the commands, pipelines, and workflows that took me years to collect. If you want to shortcut the process of building this muscle memory, it is all there.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>terminal</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Getting Useless AI Code Reviews (Here's How to Actually Catch Bugs)</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 21:59:49 +0000</pubDate>
      <link>https://dev.to/maxtendies/stop-getting-useless-ai-code-reviews-heres-how-to-actually-catch-bugs-3947</link>
      <guid>https://dev.to/maxtendies/stop-getting-useless-ai-code-reviews-heres-how-to-actually-catch-bugs-3947</guid>
      <description>&lt;p&gt;You paste your code into Claude or ChatGPT. You type "review this code." You get back a polite essay about how your variable names could be more descriptive and maybe you should add some comments.&lt;/p&gt;

&lt;p&gt;Congratulations, you just wasted 45 seconds getting feedback your linter already gives you for free.&lt;/p&gt;

&lt;p&gt;The dirty secret of AI-assisted code review is that most developers are asking the wrong questions. They treat the AI like a junior dev doing a drive-by review instead of what it actually is: an infinitely patient analyzer that can check your code against hundreds of failure modes simultaneously -- if you tell it to.&lt;/p&gt;

&lt;p&gt;Let me show you how to get reviews that actually find bugs.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Generic Prompts, Generic Reviews
&lt;/h2&gt;

&lt;p&gt;Here is a prompt I see developers use constantly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this code and let me know if you see any issues.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here is what you get back: a surface-level scan that mentions code style, suggests renaming a variable from &lt;code&gt;x&lt;/code&gt; to &lt;code&gt;something_more_descriptive&lt;/code&gt;, and tells you to add error handling "where appropriate." None of that prevents a 3am production incident.&lt;/p&gt;

&lt;p&gt;The AI is not being lazy. It is responding to the scope you gave it, which is "everything and nothing." When you say "any issues," the model optimizes for breadth. It gives you a little bit about style, a little about performance, a little about readability. It covers the easy stuff because you did not tell it to dig deeper.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technique 1: The Targeted Review
&lt;/h2&gt;

&lt;p&gt;Instead of asking for a general review, pick a specific failure category and go deep.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this Python function for issues.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this Python function exclusively for error handling gaps.

For each gap you find:
1. What input or condition triggers the failure
2. What happens when it fails (crash, silent corruption, wrong result)
3. A concrete fix with code

Ignore style, naming, and performance. I only care about
ways this function can break at runtime.

[paste function]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is night and day. The first prompt gets you "consider adding a try-except block." The second gets you "if &lt;code&gt;user_data&lt;/code&gt; is None, line 14 throws an AttributeError because you call &lt;code&gt;.get()&lt;/code&gt; on a NoneType; here is the guard clause you need."&lt;/p&gt;

&lt;p&gt;Run separate passes for separate concerns. One pass for error handling. One for security. One for performance. Three focused reviews beat one unfocused review every time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technique 2: Give the AI a Threat Model
&lt;/h2&gt;

&lt;p&gt;Context changes everything. Watch what happens when you tell the AI &lt;em&gt;who&lt;/em&gt; is using your code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Check this API endpoint for security issues.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This is a public-facing REST endpoint that accepts user input.
It is deployed behind an API gateway but has no rate limiting yet.
Authentication is handled via JWT tokens passed in the Authorization header.
The endpoint writes to a PostgreSQL database using parameterized queries.

Assume the attacker is an authenticated user trying to:
- Access other users' data
- Escalate their permissions
- Cause denial of service
- Exfiltrate data through the response

Review this endpoint code. For each vulnerability found,
rate it Critical/High/Medium/Low and show the exploit scenario.

[paste code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now instead of getting "make sure you sanitize inputs" (thanks, very helpful), you get findings like: "An authenticated user can modify the &lt;code&gt;user_id&lt;/code&gt; parameter to access other users' records because line 23 uses the request parameter instead of extracting the ID from the JWT token. This is a Critical IDOR vulnerability."&lt;/p&gt;

&lt;p&gt;That is a finding you can actually act on.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technique 3: The Before/After Diff Review
&lt;/h2&gt;

&lt;p&gt;This one is underused. Instead of reviewing a whole file, give the AI the change and let it focus on what is new.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'm modifying this function to add caching. Review ONLY my changes
for correctness. Here is the original:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
python&lt;br&gt;
def get_user(user_id: str) -&amp;gt; User:&lt;br&gt;
    return db.query(User).filter(User.id == user_id).first()&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Here is my updated version:

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
python&lt;br&gt;
_cache = {}&lt;/p&gt;

&lt;p&gt;def get_user(user_id: str) -&amp;gt; User:&lt;br&gt;
    if user_id in _cache:&lt;br&gt;
        return _cache[user_id]&lt;br&gt;
    user = db.query(User).filter(User.id == user_id).first()&lt;br&gt;
    _cache[user_id] = user&lt;br&gt;
    return user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
Specifically check for:
1. Cache invalidation problems
2. Thread safety issues
3. Memory leak potential
4. Cases where stale data causes bugs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where AI code review genuinely shines. It will immediately flag that the cache never invalidates, that a module-level mutable dict is not thread-safe in a multi-worker setup, and that deleted or updated users will serve stale data forever. Those are three real bugs you might not catch in a self-review because you are focused on whether the caching logic itself works.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technique 4: The Adversarial Edge Case Finder
&lt;/h2&gt;

&lt;p&gt;This is my favorite technique for functions that handle user input or complex business logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Here is a function that calculates shipping costs.
Your job is to break it. 

Find inputs that produce:
- Wrong results
- Crashes or exceptions  
- Infinite loops or hangs
- Negative or nonsensical values

For each breaking input, show:
1. The exact input values
2. What the function returns or throws
3. What it SHOULD do instead

[paste function]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Telling the AI to "break" your code activates a completely different analytical mode than asking it to "review" your code. Review mode is polite and surface-level. Break mode is adversarial and thorough. It will try zero, negative numbers, None values, absurdly large inputs, unicode strings where you expected ASCII, empty collections, and every other edge case you forgot about.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technique 5: The Production Failure Premortem
&lt;/h2&gt;

&lt;p&gt;This one saves you from the bugs that only show up at scale.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This code works in my tests. Imagine it is running in production
with 10,000 requests per minute. What breaks?

Consider:
- Race conditions
- Memory pressure
- Database connection exhaustion  
- Cascading failures if a dependency goes down
- What happens during deployment (old and new code running simultaneously)

[paste code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most developers test the happy path at low volume. This prompt forces the AI to simulate production conditions mentally and identify the failure modes that only emerge under load, during deployments, or when external services degrade. The deployment scenario alone -- old code and new code running side by side -- catches schema migration issues that are invisible in single-instance testing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Meta-Technique: Iterate, Don't Restart
&lt;/h2&gt;

&lt;p&gt;When the AI gives you a review, do not just read it and move on. Push back.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Good findings. Now assume those are all fixed.
What is the NEXT most subtle bug you can find?
Dig deeper -- look for logic errors, off-by-one mistakes,
and assumptions that hold in tests but not in production.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first pass catches the obvious stuff. The second pass, when you explicitly tell it to go deeper, catches the subtle stuff. I have found legitimate production bugs on the third iteration of this loop that I would never have caught manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why This Works
&lt;/h2&gt;

&lt;p&gt;All of these techniques share the same underlying principle: &lt;strong&gt;specificity creates depth&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A vague prompt gives the AI permission to be vague back. A specific prompt with a defined scope, a threat model, a failure category, and a required output format forces the AI to do actual analysis instead of pattern-matching against common review feedback.&lt;/p&gt;

&lt;p&gt;The difference between "review this code" and a well-structured review prompt is the difference between a rubber stamp and a genuine code audit. Same AI, same code, completely different results.&lt;/p&gt;




&lt;h2&gt;
  
  
  Skip the Trial and Error
&lt;/h2&gt;

&lt;p&gt;It took me months of daily iteration to figure out which prompt structures consistently surface real bugs versus generic noise. If you want to shortcut that process, I put together a &lt;a href="https://maxtendies.gumroad.com/l/claude-code-prompt-pack" rel="noopener noreferrer"&gt;Claude Code Prompt Pack&lt;/a&gt; with 50+ battle-tested prompts covering code review, security audits, debugging, architecture analysis, performance optimization, and more. Each prompt includes the template, usage notes, and tips on how to iterate for deeper results. It is the reference I wish I had when I started using AI for serious code work.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>codereview</category>
      <category>programming</category>
      <category>productivity</category>
    </item>
    <item>
      <title>7 Financial Mistakes That Almost Killed My Freelance Dev Career (Don't Repeat Them)</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 21:54:02 +0000</pubDate>
      <link>https://dev.to/maxtendies/7-financial-mistakes-that-almost-killed-my-freelance-dev-career-dont-repeat-them-4gi8</link>
      <guid>https://dev.to/maxtendies/7-financial-mistakes-that-almost-killed-my-freelance-dev-career-dont-repeat-them-4gi8</guid>
      <description>&lt;p&gt;I made $92,000 in my first year of freelancing. I also nearly went broke.&lt;/p&gt;

&lt;p&gt;If that sounds contradictory, congratulations -- you already understand more about freelance finances than I did when I quit my full-time job and started taking client work. Turns out, making money and keeping money are two completely different skills. And nobody teaches the second one in a coding bootcamp.&lt;/p&gt;

&lt;p&gt;Here are the seven financial mistakes that nearly tanked my freelance career, and exactly how I fixed each one.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Not Tracking Expenses (Then Getting Wrecked at Tax Time)
&lt;/h2&gt;

&lt;p&gt;My first year, I kept zero records. I bought software subscriptions, a new monitor, a coworking membership, courses, domain names -- all from a mix of personal cards and PayPal. When my accountant asked for my business expenses in March, I spent an entire weekend digging through bank statements, email receipts, and PayPal transaction logs.&lt;/p&gt;

&lt;p&gt;I found about $6,200 in deductible expenses. My accountant estimated I probably missed another $2,000 to $3,000 worth. At a 30% effective tax rate, that's roughly $800 I lit on fire by being too lazy to keep a spreadsheet.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Log every business expense the day it happens. It takes 30 seconds. I use a simple spreadsheet with columns for date, vendor, amount, category, and whether it's deductible. At tax time, I just hand over the sheet.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Undercharging Because I Was Scared of Hearing "No"
&lt;/h2&gt;

&lt;p&gt;My first freelance rate was $50/hour. I picked it because it felt like a lot more than my salaried equivalent, and I was terrified that a higher number would scare clients away.&lt;/p&gt;

&lt;p&gt;Here's what I didn't account for: self-employment tax (15.3%), health insurance ($400/month), no paid vacation, no employer 401k match, software and hardware costs, and all the unbillable hours spent on invoicing, email, proposals, and marketing. After all that, my effective rate was closer to $28/hour. Less than my old job.&lt;/p&gt;

&lt;p&gt;The clients who would have said no to $100/hour were never going to be good clients anyway. The ones who said yes at $50 were the ones who expected 24/7 availability, scope-creeped constantly, and haggled over every invoice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Calculate your minimum viable rate. Take your desired annual income, add 30% for taxes, add your business expenses, add your benefits costs, then divide by the number of billable hours you'll realistically work (hint: it's not 2,080 -- more like 1,200-1,400). The number will probably be higher than you expect. Charge it anyway.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Invoicing Late (Or Worse, Inconsistently)
&lt;/h2&gt;

&lt;p&gt;For my first six months, I would finish a project and then... forget to invoice. Sometimes for weeks. I once found a completed project that I hadn't invoiced for 40 days. The client paid eventually, but that's 40 days of my cash flow I donated to their treasury department.&lt;/p&gt;

&lt;p&gt;Worse, I had no consistent format. Some invoices were PDFs I made in Google Docs. Some were plain text emails. One was literally a Slack message that said "hey, you owe me $3,200 for the API work." Shockingly, that one took the longest to get paid.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Invoice the day you deliver. Not the day after. Not next Monday. The day you deliver. Use a consistent numbered format (INV-2026-001, INV-2026-002) so you can track what's outstanding. Include clear payment terms, your payment details, and a line-item breakdown. Clients pay professional-looking invoices faster because they look like they came from a real business, not a guy who might forget to follow up.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Not Saving for Taxes (The Classic Freelancer Trap)
&lt;/h2&gt;

&lt;p&gt;April of year one was a bloodbath. I owed $14,000 in federal taxes, plus state. I had about $3,000 in my checking account. I ended up on an IRS payment plan, which is exactly as fun as it sounds.&lt;/p&gt;

&lt;p&gt;When you're employed, taxes are invisible. Your paycheck arrives pre-shrunk. When you're freelance, every payment hits your account looking fat and happy, whispering "spend me." Then Q1 estimated taxes roll around and suddenly you're doing math you don't like.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; The moment a client payment lands, move 25-30% into a separate savings account. Don't touch it. That's the government's money -- you're just holding it temporarily. Pay estimated taxes quarterly (April 15, June 15, September 15, January 15). Yes, it hurts every time. It hurts a lot less than a surprise $14,000 bill plus penalties.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Not Tracking Billable vs. Non-Billable Hours
&lt;/h2&gt;

&lt;p&gt;I tracked hours for client work, obviously. But I didn't track all the time I spent on proposals that went nowhere, emails, bookkeeping, marketing, chasing late payments, and upgrading my dev environment.&lt;/p&gt;

&lt;p&gt;When I finally tracked everything for a month, I discovered that only about 60% of my working hours were billable. That means my effective hourly rate was 60% of whatever I was charging. A $100/hour freelancer who's only billable 60% of the time is actually making $60/hour before taxes and expenses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Track all your hours for at least one month, categorized by billable client work, sales and proposals, admin and bookkeeping, and professional development. This gives you your real billable ratio, which you need for setting rates that actually sustain a business. If your ratio is below 65%, you either need to raise your rates or find ways to reduce admin overhead.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. No Client Pipeline (Feast or Famine Cycle)
&lt;/h2&gt;

&lt;p&gt;For my first 18 months, my revenue looked like an EKG readout. $12,000 one month. $2,000 the next. $0 the month after that. Then $15,000 when I panic-landed a big project.&lt;/p&gt;

&lt;p&gt;The pattern was always the same: get a client, bury myself in the work, deliver, look up, realize I have no pipeline, spend three weeks scrambling for the next gig while my savings drain. Repeat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Even when you're busy, keep your pipeline warm. Dedicate at least 2-3 hours per week to business development -- responding to leads, maintaining referral relationships, creating content, or following up with past clients about upcoming needs. The best time to find your next client is when you don't need one yet, because desperation is not a negotiation strategy.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Treating It Like a Job Instead of a Business
&lt;/h2&gt;

&lt;p&gt;This one took me the longest to learn. I was thinking like an employee: do the work, get paid, repeat. I wasn't thinking about profit margins, client lifetime value, which types of projects were most profitable, or which clients were silently eating my margins through scope creep and slow payments.&lt;/p&gt;

&lt;p&gt;Once I started tracking real metrics -- revenue per client, average project profitability, days-to-payment, effective hourly rate after expenses -- I realized that two of my five clients were generating 80% of my profit, and one was actually costing me money when I factored in all the unpaid revision cycles.&lt;/p&gt;

&lt;p&gt;I fired the unprofitable client. My income went up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Look at your freelance practice as a business, not a series of gigs. Track the numbers. Know which clients and project types are profitable. Know your monthly overhead. Know your effective rate. Make decisions based on data, not gut feel.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Common Thread
&lt;/h2&gt;

&lt;p&gt;Every single one of these mistakes has the same root cause: I didn't have systems.&lt;/p&gt;

&lt;p&gt;I was a great developer. I could ship code, debug production issues at 2am, architect scalable systems. But I was running a business on vibes. No tracking, no dashboards, no process.&lt;/p&gt;

&lt;p&gt;The fix wasn't complicated. It was a spreadsheet. A few hours of setup, a few minutes of daily maintenance, and suddenly I could see where my money was coming from, where it was going, and what I owed in taxes -- all in real time. No more surprises.&lt;/p&gt;

&lt;p&gt;After three years of refining my system, I've turned it into a kit that any freelance developer can use from day one: a client and project tracker, an invoice generator, an income and expense dashboard, and a tax prep summary -- all connected so data flows between them automatically. I've packaged it as the &lt;a href="https://maxtendies.gumroad.com/l/freelance-developer-business-kit" rel="noopener noreferrer"&gt;Freelance Developer Business Kit&lt;/a&gt;. It's the system I wish I'd had before I donated $800 to the IRS in missed deductions and learned what a quarterly estimated payment was the hard way.&lt;/p&gt;

&lt;p&gt;Don't make the same mistakes I did. Your code is solid. Make sure your business is too.&lt;/p&gt;

</description>
      <category>freelancing</category>
      <category>career</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Docker Commands You Keep Googling (A Cheatsheet for the Rest of Us)</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 21:53:58 +0000</pubDate>
      <link>https://dev.to/maxtendies/docker-commands-you-keep-googling-a-cheatsheet-for-the-rest-of-us-2dck</link>
      <guid>https://dev.to/maxtendies/docker-commands-you-keep-googling-a-cheatsheet-for-the-rest-of-us-2dck</guid>
      <description>&lt;p&gt;I have mass-deployed Kubernetes clusters. I have written multi-stage Dockerfiles that would make a build engineer weep with joy. And yet, at least once a week, I open a browser tab and type "docker remove all stopped containers" like it is the first time I have ever touched a terminal.&lt;/p&gt;

&lt;p&gt;You do it too. We all do it. Docker's CLI is powerful, but it was clearly designed by someone who thought consistency was optional. Some commands use &lt;code&gt;container&lt;/code&gt;, some use &lt;code&gt;ps&lt;/code&gt;, some have flags that mean entirely different things depending on context.&lt;/p&gt;

&lt;p&gt;So here is the article I wish existed three years ago: the Docker commands that experienced developers actually forget, organized by the real-world situations where you need them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 1: "Something Is Eating All My Disk Space"
&lt;/h2&gt;

&lt;p&gt;You get a disk space alert, or your laptop starts wheezing. Nine times out of ten, Docker is hoarding old images, stopped containers, and orphaned volumes like a digital packrat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;See how much space Docker is consuming:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system &lt;span class="nb"&gt;df&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives you a breakdown by images, containers, volumes, and build cache. The output will probably horrify you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The nuclear option -- reclaim everything:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This removes all stopped containers, all networks not used by at least one container, all images without at least one container associated to them, and all volumes not used by at least one container. It is the &lt;code&gt;rm -rf&lt;/code&gt; of Docker. Use it on your dev machine, not in production. Obviously.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The surgical approach -- clean up one category at a time:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove stopped containers only&lt;/span&gt;
docker container prune

&lt;span class="c"&gt;# Remove dangling images (untagged layers)&lt;/span&gt;
docker image prune

&lt;span class="c"&gt;# Remove ALL unused images, not just dangling ones&lt;/span&gt;
docker image prune &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# Remove unused volumes (careful -- this includes data)&lt;/span&gt;
docker volume prune
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I run &lt;code&gt;docker system df&lt;/code&gt; every Monday morning. It is a surprisingly effective habit for keeping your dev environment from bloating to 50GB of stale Alpine images.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 2: "What Is Actually Running Right Now?"
&lt;/h2&gt;

&lt;p&gt;You are trying to start a service and port 3000 is already taken. Or something is chewing CPU. Time to investigate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# What containers are running?&lt;/span&gt;
docker ps

&lt;span class="c"&gt;# What containers exist, including stopped ones?&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-a&lt;/span&gt;

&lt;span class="c"&gt;# Just give me the IDs (useful for scripting)&lt;/span&gt;
docker ps &lt;span class="nt"&gt;-q&lt;/span&gt;

&lt;span class="c"&gt;# Real-time resource usage for all running containers&lt;/span&gt;
docker stats

&lt;span class="c"&gt;# Resource usage for a specific container&lt;/span&gt;
docker stats my-container-name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;docker stats&lt;/code&gt; command is underrated. It gives you a live-updating table with CPU percentage, memory usage, network I/O, and disk I/O for every running container. It is basically &lt;code&gt;htop&lt;/code&gt; for Docker and it is built in. No extra tools needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding which container grabbed your port:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker ps &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s2"&gt;"table {{.Names}}&lt;/span&gt;&lt;span class="se"&gt;\t&lt;/span&gt;&lt;span class="s2"&gt;{{.Ports}}"&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;--format&lt;/code&gt; flag with Go templates is one of Docker's best-kept secrets. You can customize the output of almost any list command instead of parsing walls of text.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 3: "The Container Crashed and I Need to Know Why"
&lt;/h2&gt;

&lt;p&gt;The container started, ran for 3 seconds, and died. The classic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# View logs for a stopped or running container&lt;/span&gt;
docker logs my-container

&lt;span class="c"&gt;# Follow logs in real-time (like tail -f)&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-f&lt;/span&gt; my-container

&lt;span class="c"&gt;# Show only the last 50 lines&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--tail&lt;/span&gt; 50 my-container

&lt;span class="c"&gt;# Show logs with timestamps&lt;/span&gt;
docker logs &lt;span class="nt"&gt;-t&lt;/span&gt; my-container

&lt;span class="c"&gt;# Show logs since a specific time&lt;/span&gt;
docker logs &lt;span class="nt"&gt;--since&lt;/span&gt; 2h my-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--since&lt;/code&gt; flag accepts human-readable durations like &lt;code&gt;2h&lt;/code&gt;, &lt;code&gt;30m&lt;/code&gt;, or &lt;code&gt;10s&lt;/code&gt;. That alone saves me from scrolling through 10,000 lines of health check output to find the one error that matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If logs are not enough, inspect the container's state:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# See exit code, error message, environment, mounts, everything&lt;/span&gt;
docker inspect my-container

&lt;span class="c"&gt;# Just the exit code&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{.State.ExitCode}}'&lt;/span&gt; my-container

&lt;span class="c"&gt;# Just the error message&lt;/span&gt;
docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{.State.Error}}'&lt;/span&gt; my-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit code 137 means it was killed (usually OOM). Exit code 1 means your application crashed. Exit code 0 means it exited normally, and if that was not intentional, your entrypoint script is probably finishing without starting a long-running process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 4: "I Need to Get Inside This Container"
&lt;/h2&gt;

&lt;p&gt;Something is wrong and you need to poke around inside the running container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Open a bash shell in a running container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-container bash

&lt;span class="c"&gt;# If bash is not installed (common in Alpine images)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-container sh

&lt;span class="c"&gt;# Run a single command without opening a shell&lt;/span&gt;
docker &lt;span class="nb"&gt;exec &lt;/span&gt;my-container &lt;span class="nb"&gt;cat&lt;/span&gt; /etc/hosts

&lt;span class="c"&gt;# Run as root (when the container runs as non-root)&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; root &lt;span class="nt"&gt;-it&lt;/span&gt; my-container bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;-u root&lt;/code&gt; trick is one I forget constantly. Many production images run as a non-root user for security, which means &lt;code&gt;apt-get install&lt;/code&gt; and similar debugging commands will fail. Switching to root inside the container lets you install &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;netcat&lt;/code&gt;, or whatever else you need to debug with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For containers that already crashed and will not start:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a new container from the same image with a shell&lt;/span&gt;
docker run &lt;span class="nt"&gt;-it&lt;/span&gt; &lt;span class="nt"&gt;--entrypoint&lt;/span&gt; sh my-image:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overriding the entrypoint lets you poke around the filesystem even if the normal startup command fails immediately.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 5: "Docker Compose Is Doing Something Weird"
&lt;/h2&gt;

&lt;p&gt;Compose has its own set of commands that overlap with but are not identical to regular Docker commands. This is where my memory fails me most often.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Start everything in the background&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Rebuild images AND start (when you changed a Dockerfile)&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--build&lt;/span&gt;

&lt;span class="c"&gt;# Start only one specific service&lt;/span&gt;
docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt; postgres

&lt;span class="c"&gt;# Stop everything but keep volumes&lt;/span&gt;
docker compose down

&lt;span class="c"&gt;# Stop everything AND destroy volumes (full reset)&lt;/span&gt;
docker compose down &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="c"&gt;# View logs for one service&lt;/span&gt;
docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt; api

&lt;span class="c"&gt;# Run a one-off command in a service container&lt;/span&gt;
docker compose &lt;span class="nb"&gt;exec &lt;/span&gt;api python manage.py migrate

&lt;span class="c"&gt;# Run a one-off command in a NEW container&lt;/span&gt;
docker compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; api python manage.py shell
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference between &lt;code&gt;exec&lt;/code&gt; and &lt;code&gt;run&lt;/code&gt; in Compose trips people up. &lt;code&gt;exec&lt;/code&gt; runs a command in an already-running container. &lt;code&gt;run&lt;/code&gt; spins up a new container for the command. Use &lt;code&gt;exec&lt;/code&gt; for quick commands against your running stack. Use &lt;code&gt;run&lt;/code&gt; for tasks that might mess up the running container's state, like database migrations during development.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 6: "I Need to Move an Image Without a Registry"
&lt;/h2&gt;

&lt;p&gt;You are working offline, or you need to transfer an image to a machine that cannot reach your registry. This comes up more often than you would expect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Save an image to a tar file&lt;/span&gt;
docker save &lt;span class="nt"&gt;-o&lt;/span&gt; myapp.tar myapp:latest

&lt;span class="c"&gt;# Load an image from a tar file&lt;/span&gt;
docker load &lt;span class="nt"&gt;-i&lt;/span&gt; myapp.tar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two commands. I Google them every single time. The asymmetry of &lt;code&gt;save&lt;/code&gt;/&lt;code&gt;load&lt;/code&gt; versus the &lt;code&gt;export&lt;/code&gt;/&lt;code&gt;import&lt;/code&gt; pair (which works on containers, not images) is exactly the kind of design choice that keeps Stack Overflow in business.&lt;/p&gt;




&lt;h2&gt;
  
  
  Situation 7: "Network Debugging Between Containers"
&lt;/h2&gt;

&lt;p&gt;Containers cannot talk to each other. The dreaded &lt;code&gt;connection refused&lt;/code&gt; or &lt;code&gt;could not resolve host&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# List all Docker networks&lt;/span&gt;
docker network &lt;span class="nb"&gt;ls&lt;/span&gt;

&lt;span class="c"&gt;# See which containers are on a network&lt;/span&gt;
docker network inspect bridge

&lt;span class="c"&gt;# Create a custom network (containers can resolve each other by name)&lt;/span&gt;
docker network create my-network

&lt;span class="c"&gt;# Connect a running container to a network&lt;/span&gt;
docker network connect my-network my-container

&lt;span class="c"&gt;# Test connectivity from inside a container&lt;/span&gt;
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; my-container ping other-container
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the thing that bites everyone at least once: containers on the default &lt;code&gt;bridge&lt;/code&gt; network cannot resolve each other by container name. You need a custom network for that. Create one, attach your containers to it, and name resolution works automatically. I have seen senior engineers waste hours on this.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Commands I Actually Have Memorized
&lt;/h2&gt;

&lt;p&gt;After years of Docker usage, these are the only commands that live permanently in my muscle memory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docker ps&lt;/code&gt; -- what is running?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker logs -f&lt;/code&gt; -- what is it saying?&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker exec -it ... bash&lt;/code&gt; -- let me look inside&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker compose up -d&lt;/code&gt; -- start everything&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker compose down&lt;/code&gt; -- stop everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is five commands. Everything else, I look up. And honestly, that is fine. The goal is not to memorize every flag. The goal is to know what is possible and find the right command quickly when you need it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stop Googling the Same Commands
&lt;/h2&gt;

&lt;p&gt;If you are tired of the browser-tab-per-Docker-question workflow, I put together a &lt;a href="https://maxtendies.gumroad.com/l/developer-cli-cheatsheet-bundle" rel="noopener noreferrer"&gt;CLI Cheatsheet Bundle&lt;/a&gt; covering Docker, Git, and Linux commands in a single searchable reference. Every command includes the flags you actually use, organized by real-world scenario instead of alphabetical order. It is the kind of thing you keep open in a second monitor and wonder how you worked without it.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why Your AI Prompts Suck (And the Framework That Fixes Them)</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 10:37:07 +0000</pubDate>
      <link>https://dev.to/maxtendies/why-your-ai-prompts-suck-and-the-framework-that-fixes-them-20m7</link>
      <guid>https://dev.to/maxtendies/why-your-ai-prompts-suck-and-the-framework-that-fixes-them-20m7</guid>
      <description>&lt;p&gt;Most developers use AI tools the same way they'd ask a coworker: vaguely. And they get vague answers back.&lt;/p&gt;

&lt;p&gt;"Hey, can you review this code?"&lt;br&gt;
"Can you help me debug this?"&lt;br&gt;
"Write me some tests."&lt;/p&gt;

&lt;p&gt;The AI responds with something generic, you get frustrated, and you go back to doing it manually. Then you tell your friends AI coding tools are overhyped.&lt;/p&gt;

&lt;p&gt;The problem isn't the AI. It's the prompt.&lt;/p&gt;




&lt;h2&gt;
  
  
  The SCOC Framework
&lt;/h2&gt;

&lt;p&gt;After testing hundreds of prompts across Claude, ChatGPT, and GitHub Copilot, I noticed a pattern. The prompts that consistently produce useful output all share four properties. I call it &lt;strong&gt;SCOC&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;S&lt;/strong&gt;tructure&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;ontext&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O&lt;/strong&gt;utput format&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt;onstraints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me break each one down.&lt;/p&gt;




&lt;h2&gt;
  
  
  S - Structure
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Review my code and tell me if there are any issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Review this code for:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Bugs and runtime errors&lt;/li&gt;
&lt;li&gt;Security vulnerabilities&lt;/li&gt;
&lt;li&gt;Performance issues&lt;/li&gt;
&lt;li&gt;Edge cases not handled&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;Why the difference matters: the structured prompt creates &lt;strong&gt;four parallel evaluation paths&lt;/strong&gt;. The AI literally checks each category separately instead of doing a single pass and mentioning whatever jumps out first.&lt;/p&gt;

&lt;p&gt;Without structure, you get a review that fixates on whitespace while missing a SQL injection vulnerability.&lt;/p&gt;




&lt;h2&gt;
  
  
  C - Context
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why doesn't this work?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This function should return user data from the database but returns None.&lt;br&gt;
It worked yesterday before I updated the ORM.&lt;br&gt;
Using Python 3.11, SQLAlchemy 2.0, PostgreSQL 15.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Context is the single biggest lever. The same code snippet produces completely different analysis depending on whether you say "this is a hobby project" vs "this handles financial transactions in production."&lt;/p&gt;

&lt;p&gt;Three types of context that matter most:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What it should do&lt;/strong&gt; (expected behavior)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What changed&lt;/strong&gt; (when did it break?)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What environment&lt;/strong&gt; (language version, framework, OS)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  O - Output Format
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give me test cases for this function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Give me test cases for this function in Jest format.&lt;br&gt;
Use descriptive test names that explain the scenario.&lt;br&gt;
Group by: happy path, edge cases, error conditions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you don't specify the output format, the AI guesses. Sometimes it gives you pseudocode when you want real code. Sometimes it gives you a wall of text when you want a checklist.&lt;/p&gt;

&lt;p&gt;Useful output formats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Checklist&lt;/strong&gt; - for review tasks, test planning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ranked list&lt;/strong&gt; - for debugging (most likely cause first)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code&lt;/strong&gt; - specify the language, framework, and test runner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Table&lt;/strong&gt; - for comparisons, trade-off analysis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step-by-step&lt;/strong&gt; - for debugging walkthroughs&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  C - Constraints
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refactor this function.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Refactor this function.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don't change the public API or return type.&lt;/li&gt;
&lt;li&gt;Keep it under 30 lines.&lt;/li&gt;
&lt;li&gt;Preserve all existing behavior.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Constraints prevent the AI from going off the rails. Without them, a refactoring prompt might rename your function, change its signature, add unnecessary abstractions, or "optimize" it by changing behavior.&lt;/p&gt;

&lt;p&gt;The most important constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Don't change behavior&lt;/strong&gt; - for refactoring&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rate by severity&lt;/strong&gt; - for security reviews&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maximum length&lt;/strong&gt; - for documentation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Specific format&lt;/strong&gt; - for code output&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Putting It Together
&lt;/h2&gt;

&lt;p&gt;Here's a real example using SCOC:&lt;/p&gt;

&lt;h3&gt;
  
  
  Without SCOC:
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Help me debug this. It's not working.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Result: Generic advice about checking logs and restarting the server.&lt;/p&gt;

&lt;h3&gt;
  
  
  With SCOC:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Help me debug this issue systematically.

[CONTEXT]
Expected: API returns user profile with avatar URL
Actual: Returns 500 error on /api/users/:id
Changed: Updated the User model to add avatar field
Stack: Node.js 20, Express 4, Prisma ORM, PostgreSQL

[STRUCTURE]
Analyze for:
1. Database schema mismatches
2. ORM configuration issues
3. Missing migrations
4. Null handling problems

[OUTPUT]
Rank causes by probability. For each:
- What to check
- How to verify
- How to fix

[CONSTRAINTS]
- Focus on the avatar field change as the likely cause
- Don't suggest unrelated refactoring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result: Identifies that the Prisma schema wasn't regenerated after adding the avatar field, provides the exact command to run, and explains why the 500 error occurs (Prisma trying to select a column that doesn't exist in the database yet).&lt;/p&gt;

&lt;p&gt;That's the difference between 30 seconds and 30 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Common Mistakes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Too much code, not enough context&lt;/strong&gt;&lt;br&gt;
Pasting 500 lines with "what's wrong?" is like handing a doctor your entire medical history and saying "fix me." Point them to the symptom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Not specifying the output format&lt;/strong&gt;&lt;br&gt;
If you want Jest tests, say Jest. If you want a checklist, say checklist. Don't make the AI guess.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. No constraints&lt;/strong&gt;&lt;br&gt;
Every refactoring prompt should include "preserve existing behavior." Every review prompt should include severity ratings. Every debugging prompt should include "rank by likelihood."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Starting over instead of iterating&lt;/strong&gt;&lt;br&gt;
If the first response is 80% right, don't rewrite the prompt from scratch. Say "good, but also check for X" or "focus more on the security aspect."&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Skill
&lt;/h2&gt;

&lt;p&gt;Prompt engineering isn't about memorizing magic phrases. It's about clearly communicating what you want, what you know, and what format you need the answer in.&lt;/p&gt;

&lt;p&gt;The developers who get the most out of AI tools aren't using secret techniques. They're just being specific.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;If you want to skip the learning curve, I put together a &lt;a href="https://maxtendies.gumroad.com/l/claude-code-prompt-pack" rel="noopener noreferrer"&gt;collection of 50+ tested developer prompts&lt;/a&gt; using the SCOC framework - covering code review, debugging, documentation, testing, architecture, security, and performance. Each one is copy-paste ready with usage notes and pro tips.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Track Finances as a Freelance Developer (No Accounting Degree Required)</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 09:17:35 +0000</pubDate>
      <link>https://dev.to/maxtendies/how-i-track-finances-as-a-freelance-developer-no-accounting-degree-required-225n</link>
      <guid>https://dev.to/maxtendies/how-i-track-finances-as-a-freelance-developer-no-accounting-degree-required-225n</guid>
      <description>&lt;p&gt;My first year freelancing, I didn't set aside a dollar for taxes. Not one. I'd come from a salaried job where all that stuff just happened automatically, and some part of my brain figured it would keep happening. It did not keep happening.&lt;/p&gt;

&lt;p&gt;April rolled around and I owed the IRS $14,000 I didn't have. I ended up on a payment plan, which if you've never done that, is a special kind of humbling. You get letters. They're not friendly letters.&lt;/p&gt;

&lt;p&gt;That tax bill is the reason this spreadsheet system exists. Not because I'm organized. Because I got my ass kicked and needed to make sure it didn't happen again.&lt;/p&gt;

&lt;h2&gt;
  
  
  QuickBooks is Probably Overkill for You
&lt;/h2&gt;

&lt;p&gt;Every freelance finance article says the same thing: get QuickBooks, get FreshBooks, get Wave. And look, those are fine tools if you're running a landscaping company with employees and inventory and purchase orders.&lt;/p&gt;

&lt;p&gt;You're a developer. You don't have inventory. You probably don't have employees. You have a laptop, some subscriptions, and clients who may or may not pay you on time.&lt;/p&gt;

&lt;p&gt;QuickBooks is overkill for most solo devs and you'll spend more time setting it up than it saves you. I know because I tried. Spent an entire Saturday configuring categories and chart of accounts, then never opened it again.&lt;/p&gt;

&lt;p&gt;What I actually needed was dead simple: am I making money, who owes me, and how much do I owe the IRS this quarter?&lt;/p&gt;

&lt;p&gt;So I built it in Google Sheets.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 4-Sheet System
&lt;/h2&gt;

&lt;p&gt;Four sheets. They talk to each other with formulas. The whole thing takes about two minutes a day to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sheet 1: Client and Project Tracker
&lt;/h3&gt;

&lt;p&gt;Every project gets a row.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Why It Matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Client + Contact&lt;/td&gt;
&lt;td&gt;So I know who to chase about late payments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate (hourly or fixed)&lt;/td&gt;
&lt;td&gt;Compares against actual time spent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Estimated vs Actual Hours&lt;/td&gt;
&lt;td&gt;Reveals if I'm undercharging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Total Invoiced vs Paid&lt;/td&gt;
&lt;td&gt;Shows outstanding balance instantly&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Active / Completed / On Hold&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The number that changed how I price projects: &lt;strong&gt;effective hourly rate&lt;/strong&gt;. For fixed-price work, it's just &lt;code&gt;total paid / actual hours&lt;/code&gt;. I quoted $5,000 for a project once, thought it'd take 40 hours. It took 100. My effective rate was $50/hr. That's a hell of a wake-up call when you think you're charging $150.&lt;/p&gt;

&lt;p&gt;Conditional formatting does the rest. Red means someone owes me money for 30+ days. Green means done and paid. You'd be surprised how motivating it is to turn rows green.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sheet 2: Invoice Tracker
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Invoice number&lt;/td&gt;
&lt;td&gt;Sequential: INV-2026-001, 002, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amount + Tax&lt;/td&gt;
&lt;td&gt;Auto-calculated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Date Sent&lt;/td&gt;
&lt;td&gt;When I emailed it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Due Date&lt;/td&gt;
&lt;td&gt;Net 30 from send date&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status&lt;/td&gt;
&lt;td&gt;Sent / Paid / Overdue&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Days to Pay&lt;/td&gt;
&lt;td&gt;Auto-calculated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That "Days to Pay" column is the most important column in the whole system.&lt;/p&gt;

&lt;p&gt;I had a client "forget" to pay a $6,800 invoice for four months. Four months. I didn't have a contract with late fees, didn't have a system that flagged it, and honestly didn't even notice for the first six weeks because I was buried in other work. I got the money eventually, but that experience was the reason I started tracking payment speed religiously. Now I know which clients pay in a week and which ones drag it past 45 days, and that data factors into whether I take them on again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sheet 3: Income and Expense Dashboard
&lt;/h3&gt;

&lt;p&gt;Two sections, nothing fancy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Income log:&lt;/strong&gt; Every payment, tagged by client and category (client work, product sales, consulting).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Expense log:&lt;/strong&gt; Every business expense, tagged by categories that actually map to IRS Schedule C lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Software and Tools (GitHub, AWS, Figma, etc.)&lt;/li&gt;
&lt;li&gt;Hardware and Equipment&lt;/li&gt;
&lt;li&gt;Home Office&lt;/li&gt;
&lt;li&gt;Professional Development&lt;/li&gt;
&lt;li&gt;Marketing&lt;/li&gt;
&lt;li&gt;Subcontractors&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The dashboard auto-generates charts and one number I care about more than any other: &lt;strong&gt;effective hourly rate including non-billable time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If I billed 120 hours at $150/hr but worked 160 hours total counting admin, marketing, and learning, my real rate is $112.50. That's the actual number. The other one is a fantasy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sheet 4: Tax Prep Summary
&lt;/h3&gt;

&lt;p&gt;This is the sheet that exists because of the $14k mistake.&lt;/p&gt;

&lt;p&gt;It auto-populates from the other three sheets. Quarterly income, deductible expenses, estimated self-employment tax at 15.3%, estimated federal and state. And the one number I actually need: &lt;strong&gt;what to send the IRS this quarter&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Before this system, I'd either overpay estimates and tie up cash I needed, or underpay and get hit with penalties. Now I'm within 5% every quarter. My accountant actually complimented the organization, which is probably the only compliment I've ever gotten from an accountant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two Minutes a Day
&lt;/h2&gt;

&lt;p&gt;Log hours. Log expenses. Mark payments received.&lt;/p&gt;

&lt;p&gt;That's it. The dashboard handles everything else. I do it at the end of the day before I close my laptop. If I skip a day, I do two days the next morning. The key is not letting it pile up, because once you're behind a month on this stuff, you're right back to the weekend-before-taxes scramble.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a Year of Data Taught Me
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;I was undercharging for fixed-price work.&lt;/strong&gt; My effective rate on fixed projects was 40% below my hourly rate. Forty percent. I was underestimating scope every single time. Raised my fixed quotes by 30% and started padding estimates by 20%. Still occasionally get burned but it's way better.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Software expenses are a slow bleed.&lt;/strong&gt; I was spending $340/month on subscriptions. Cancelled the ones I barely touched and saved $120/month. That's $1,440 a year on tools I wasn't using.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Some clients cost you money.&lt;/strong&gt; One client paid 45+ days late every time, wanted endless revisions, and my effective rate with them was literally half my normal rate. I didn't renew. Best decision I made all year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tax prep went from a full weekend to five minutes.&lt;/strong&gt; When everything is categorized throughout the year, quarterly prep is just reading the summary and writing a check. That alone is worth maintaining the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Sheets and Not Some App
&lt;/h2&gt;

&lt;p&gt;Free. Customizable. Works on my phone. My accountant can view it. No vendor lock-in. No loading screens.&lt;/p&gt;

&lt;p&gt;Yeah, the initial setup takes a few hours. Building the formulas and conditional formatting isn't trivial. That's the one real downside.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Formulas
&lt;/h2&gt;

&lt;p&gt;If you want to build your own, these are the ones that matter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Effective Hourly Rate:
= Total Paid / Actual Hours

Monthly Profit:
= SUMIFS(income, month, currentMonth) - SUMIFS(expenses, month, currentMonth)

Outstanding Balance:
= Total Invoiced - Total Paid

Quarterly Tax Estimate:
= (Quarterly Net Income * 0.153) + (Quarterly Net Income * Federal Rate)

Days to Pay:
= Date Paid - Date Sent

Average Client Lifetime Value:
= AVERAGEIFS(Total Paid, Status, "Completed")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;I built this system for myself after the IRS payment plan situation. Used it for a couple years, kept refining it. Other dev friends kept asking for a copy, so I eventually cleaned it up with a setup guide and put it on Gumroad. It's the &lt;a href="https://maxtendies.gumroad.com/l/freelance-developer-business-kit" rel="noopener noreferrer"&gt;Freelance Developer Business Kit&lt;/a&gt; if you want to skip the setup hours. Or build your own. I genuinely don't care either way, just track your damn finances.&lt;/p&gt;

</description>
      <category>freelancing</category>
      <category>career</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The Git Commands I Use Every Day (and the Ones I Always Forget)</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 09:11:04 +0000</pubDate>
      <link>https://dev.to/maxtendies/the-git-commands-i-use-every-day-and-the-ones-i-always-forget-424e</link>
      <guid>https://dev.to/maxtendies/the-git-commands-i-use-every-day-and-the-ones-i-always-forget-424e</guid>
      <description>&lt;p&gt;I force pushed to main once. Second month at my first real dev job. Just sat there watching Slack light up while the senior engineer scrambled to fix what I'd done. He was cool about it. We're still friends. I still feel the shame.&lt;/p&gt;

&lt;p&gt;That was the day I stopped treating Git like something I'd "figure out eventually" and started actually learning the commands. Seven years later, I still Google half of them. Not because I'm bad at Git. Because Git has roughly 150 commands with thousands of flag combinations and whoever designed the CLI was apparently allergic to consistency.&lt;/p&gt;

&lt;p&gt;So I keep a cheatsheet. Here's what's actually in it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Commands I Use Every Single Day
&lt;/h2&gt;

&lt;p&gt;You already know these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status                    &lt;span class="c"&gt;# What changed?&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;                     &lt;span class="c"&gt;# Stage everything&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"message"&lt;/span&gt;       &lt;span class="c"&gt;# Commit&lt;/span&gt;
git push                      &lt;span class="c"&gt;# Ship it&lt;/span&gt;
git pull                      &lt;span class="c"&gt;# Get latest&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature-name  &lt;span class="c"&gt;# New branch&lt;/span&gt;
git log &lt;span class="nt"&gt;--oneline&lt;/span&gt;             &lt;span class="c"&gt;# Quick history&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That covers maybe 80% of my day. The other 20% is where things get annoying.&lt;/p&gt;




&lt;h2&gt;
  
  
  Commands I Use Weekly (but Can Never Remember the Flags)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Stashing with a message
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git stash push &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"WIP: refactoring auth"&lt;/span&gt;  &lt;span class="c"&gt;# Named stash&lt;/span&gt;
git stash list                              &lt;span class="c"&gt;# What did I stash?&lt;/span&gt;
git stash pop                               &lt;span class="c"&gt;# Apply and remove&lt;/span&gt;
git stash apply stash@&lt;span class="o"&gt;{&lt;/span&gt;2&lt;span class="o"&gt;}&lt;/span&gt;                   &lt;span class="c"&gt;# Apply specific one&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I forget the &lt;code&gt;push -m&lt;/code&gt; syntax every single time. Plain &lt;code&gt;git stash&lt;/code&gt; works but then you end up with a list of unnamed stashes and zero idea what's in any of them. Future you will hate past you for this.&lt;/p&gt;

&lt;h3&gt;
  
  
  Interactive add
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nt"&gt;-p&lt;/span&gt;   &lt;span class="c"&gt;# Stage individual hunks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is genuinely the most underused Git command. It lets you stage &lt;em&gt;parts&lt;/em&gt; of a file instead of the whole thing. If you've ever had a messy working directory and wanted clean, logical commits instead of one giant "WIP" commit, this is how you do it.&lt;/p&gt;

&lt;p&gt;Most people don't know it exists. I didn't for years.&lt;/p&gt;

&lt;h3&gt;
  
  
  Viewing diffs properly
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git diff                      &lt;span class="c"&gt;# Unstaged changes&lt;/span&gt;
git diff &lt;span class="nt"&gt;--staged&lt;/span&gt;             &lt;span class="c"&gt;# What's about to be committed&lt;/span&gt;
git diff HEAD                 &lt;span class="c"&gt;# Everything since last commit&lt;/span&gt;
git diff main..feature        &lt;span class="c"&gt;# Branch comparison&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I mix up &lt;code&gt;--staged&lt;/code&gt; and &lt;code&gt;--cached&lt;/code&gt; every time. They're the same thing. &lt;code&gt;--staged&lt;/code&gt; is just the modern alias. Why Git has two flags that do the same thing is beyond me, but here we are.&lt;/p&gt;




&lt;h2&gt;
  
  
  Commands I Look Up Every Time
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Undoing the last commit
&lt;/h3&gt;

&lt;p&gt;This one is a pain in the ass to remember because there are three different ways to do it and they all do different things.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Keep changes (just undo the commit)&lt;/span&gt;
git reset &lt;span class="nt"&gt;--soft&lt;/span&gt; HEAD~1

&lt;span class="c"&gt;# Discard everything (nuclear option)&lt;/span&gt;
git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; HEAD~1

&lt;span class="c"&gt;# Undo but create a new "undo" commit (safe for shared branches)&lt;/span&gt;
git revert HEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reset&lt;/strong&gt; rewrites history. Never use it on shared branches. I learned this the hard way (see: the force push story above). &lt;strong&gt;Revert&lt;/strong&gt; creates a new commit that undoes the old one. Safe for shared branches. Use revert if anyone else has pulled that code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cherry-picking
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git cherry-pick abc123f       &lt;span class="c"&gt;# Apply one commit to current branch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use this maybe twice a month. The syntax is dead simple and I still look it up every time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finding what broke things
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git bisect start
git bisect bad                &lt;span class="c"&gt;# Current commit is broken&lt;/span&gt;
git bisect good abc123f       &lt;span class="c"&gt;# This older commit was fine&lt;/span&gt;
&lt;span class="c"&gt;# Git checks out a middle commit - test it, then:&lt;/span&gt;
git bisect good               &lt;span class="c"&gt;# or git bisect bad&lt;/span&gt;
&lt;span class="c"&gt;# Repeat until Git finds the breaking commit&lt;/span&gt;
git bisect reset              &lt;span class="c"&gt;# Done, go back to normal&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;git bisect&lt;/code&gt; is magic. Binary search through your commit history. For a 1000-commit range, it finds the exact commit that broke things in about 10 steps. I wish I'd known about this earlier. Would have saved me a lot of 2am &lt;code&gt;git log&lt;/code&gt; scrolling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleaning up merged branches
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Delete local branch&lt;/span&gt;
git branch &lt;span class="nt"&gt;-d&lt;/span&gt; branch-name

&lt;span class="c"&gt;# Delete remote branch&lt;/span&gt;
git push origin &lt;span class="nt"&gt;--delete&lt;/span&gt; branch-name

&lt;span class="c"&gt;# Remove stale remote tracking branches&lt;/span&gt;
git fetch &lt;span class="nt"&gt;--prune&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just run &lt;code&gt;git fetch --prune&lt;/code&gt; regularly. Your branch list is probably a mess right now. Go look. I'll wait.&lt;/p&gt;

&lt;h3&gt;
  
  
  The reflog (your safety net)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reflog                    &lt;span class="c"&gt;# Show everything you've done&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; recovery abc123f  &lt;span class="c"&gt;# Recover "lost" commits&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even after a hard reset, your commits aren't truly gone for about 30 days. The reflog tracks every HEAD movement. This is the command that would have saved me from that force push panic at my old job if I'd known it existed at the time. I didn't. The senior dev did, though. That's how he bailed me out.&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Actually Remember Things
&lt;/h2&gt;

&lt;p&gt;I don't.&lt;/p&gt;

&lt;p&gt;The few commands I've memorized are the ones I type 10+ times a day. Everything else, I look up. The trick isn't memorizing all of this. It's having a reference you can search in 5 seconds instead of opening 12 Stack Overflow tabs.&lt;/p&gt;

&lt;p&gt;I keep mine organized by what I'm trying to do, not alphabetically by command name. Because when something's broken at 11pm, I'm not thinking "what's the syntax for revert," I'm thinking "how do I undo this commit without destroying everything."&lt;/p&gt;

&lt;p&gt;I ended up putting together a &lt;a href="https://maxtendies.gumroad.com/l/developer-cli-cheatsheet-bundle" rel="noopener noreferrer"&gt;Developer CLI Cheatsheet Bundle&lt;/a&gt; based on my own reference doc. Git, Docker, Linux, organized by workflow. Honestly built it for myself first and figured other people probably have the same 30-tabs-open problem.&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>linux</category>
      <category>beginners</category>
    </item>
    <item>
      <title>10 Claude Prompts That Save Me Hours Every Week as a Developer</title>
      <dc:creator>max</dc:creator>
      <pubDate>Fri, 30 Jan 2026 09:09:47 +0000</pubDate>
      <link>https://dev.to/maxtendies/10-claude-prompts-that-save-me-hours-every-week-as-a-developer-4cfa</link>
      <guid>https://dev.to/maxtendies/10-claude-prompts-that-save-me-hours-every-week-as-a-developer-4cfa</guid>
      <description>&lt;p&gt;I thought AI coding tools were bullshit.&lt;/p&gt;

&lt;p&gt;Not in a loud, Twitter-argument way. More like a quiet eye-roll every time someone on a podcast said "and then I just asked the AI to refactor it." Yeah, sure you did. I'm sure that went great.&lt;/p&gt;

&lt;p&gt;Then sometime around mid-2024, I was three weeks into a freelance project and there was a bug I could not find. The logic looked right. The tests passed. But every few hundred requests, the whole thing would just... silently drop data. I was losing my mind. On a whim, I pasted the module into Claude with a prompt I'd been tinkering with and asked it to look for concurrency issues.&lt;/p&gt;

&lt;p&gt;It found a race condition in under ten seconds. A race condition I had been staring past for weeks.&lt;/p&gt;

&lt;p&gt;I didn't mass-convert to the Church of AI that day. But I did shut up about it being useless. And over the months since, I've built up a set of prompts that I actually use every day. Not cute demos. Not "look what AI can do" parlor tricks. Just prompts that save me real time on real work.&lt;/p&gt;

&lt;p&gt;Here are the ones I keep coming back to.&lt;/p&gt;




&lt;h2&gt;
  
  
  The one I use before I ship anything
&lt;/h2&gt;

&lt;p&gt;Most developers ask AI to "review my code" and wonder why the feedback is garbage. No constraints, no focus, just "hey look at this." You wouldn't walk up to a senior dev and say "look at this" with zero context. Same thing here.&lt;/p&gt;

&lt;p&gt;This is what I actually paste in before I push:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I'm about to ship this code. Give me a quick sanity check:
1. Any obvious bugs?
2. Will this break in production?
3. What's the one thing I probably forgot?

[paste code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That third question does an unreasonable amount of heavy lifting. Turns out AI is weirdly good at catching the thing you stopped thinking about two hours ago because you got tunnel vision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Debugging (but make it structured)
&lt;/h2&gt;

&lt;p&gt;I learned the hard way that "fix this" is the worst possible prompt. You get back a confident, plausible, completely wrong answer. Then you waste twenty minutes implementing it before realizing the AI hallucinated the root cause.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Help me debug this issue systematically.

Expected behavior: [what should happen]
Actual behavior: [what's happening]
Error message: [paste error]
Code: [paste relevant code]

Walk me through:
1. Most likely causes (ranked by probability)
2. How to verify each hypothesis
3. The fix for the most probable cause
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is "ranked by probability." Without that, you get a grab bag of five theories with no sense of which one to try first. With it, you get a differential diagnosis. I've had this nail the issue on the first guess more times than I expected.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security audit
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Perform a security audit on this code. Check for:
- SQL injection vulnerabilities
- XSS attack vectors
- Authentication/authorization flaws
- Sensitive data exposure
- Input validation issues

Rate each finding as Critical/High/Medium/Low.

[paste code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The severity rating is the whole trick. Without it you get a wall of text where a missing CSRF token and a hardcoded database password look equally important.&lt;/p&gt;




&lt;h2&gt;
  
  
  The error message translator
&lt;/h2&gt;

&lt;p&gt;Short one. When you get some cryptic stack trace from a library you didn't write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Explain this error in plain English and tell me how to fix it:

[paste full error + stack trace]

Context: I was trying to [what you were doing]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That context line matters more than you'd think. "I was trying to deploy to Kubernetes" and "I was running tests locally" are completely different debugging paths, and without that one line the AI will guess wrong half the time.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code review that doesn't suck
&lt;/h2&gt;

&lt;p&gt;I have strong feelings about this one. The default AI code review experience is terrible. You paste code, it says "looks good, maybe add some comments." Useless.&lt;/p&gt;

&lt;p&gt;This prompt forces it to actually look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review this code for:
1. Bugs and potential runtime errors
2. Security vulnerabilities
3. Performance issues
4. Code style and readability
5. Edge cases not handled

Provide specific line references and concrete suggestions.

[paste code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The numbered list prevents it from fixating on one category. Without it, you'll get ten nitpicks about variable naming and zero actual bug reports. The "specific line references" part is what makes the feedback actionable instead of vague.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test cases
&lt;/h2&gt;

&lt;p&gt;Look, I know writing tests after the fact sucks. I do it anyway because production incidents at 2am suck more. But brainstorming what to test is the part that takes forever, and this is where AI genuinely shines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Brainstorm test cases for this feature:

Feature: [describe it]
Inputs: [what it accepts]
Outputs: [what it produces]

Generate cases for:
- Normal usage
- Edge cases
- Invalid inputs
- Concurrency if applicable

Format as a checklist.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I don't use the generated tests as-is. I use the list as a "did I forget anything" check. And yeah, I almost always forgot something.&lt;/p&gt;




&lt;h2&gt;
  
  
  Breaking up long functions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This function is too long. Help me break it into smaller functions:
1. Identify logical sections
2. Suggest descriptive function names
3. Show the refactored code
4. Ensure behavior doesn't change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Step 4 is doing the important work. Skip it and the AI will "improve" your code by quietly changing behavior. I've been bitten by that.&lt;/p&gt;




&lt;h2&gt;
  
  
  Query optimization
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Optimize this database query:

Query: [paste SQL]
Table schemas: [paste schemas]
Current execution time: [if known]

Provide:
- Optimized query
- Suggested indexes
- Explanation of improvements
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't include the schemas it'll suggest indexes on columns that don't exist. Ask me how I know.&lt;/p&gt;




&lt;h2&gt;
  
  
  The "works on my machine" prompt
&lt;/h2&gt;

&lt;p&gt;Every developer's favorite sentence. This one has saved me from an embarrassing amount of Docker-related yelling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This code works locally but fails in production/CI/Docker.

Local environment: [describe]
Production environment: [describe]
Error in production: [paste error]

What environment differences could cause this?
Give me a checklist to verify.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Environment bugs are a pain in the ass because the code looks correct. It IS correct. It's everything around the code that's wrong. The checklist format keeps you from randomly changing config values and hoping something sticks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Documentation (the chore I actually outsource now)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Write documentation for this function:
- Purpose and description
- Parameters with types
- Return value
- Exceptions that can be thrown
- Usage example
- Edge cases to be aware of

Use your preferred doc format.

[paste function]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the one prompt where I mostly just take the output and paste it in. Not much creativity needed for JSDoc. I'd rather spend my brain on the actual code.&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern across all of these
&lt;/h2&gt;

&lt;p&gt;You probably noticed: every prompt that works well has structure, context, and constraints. You tell the AI what to look at, how to format the answer, and what not to do.&lt;/p&gt;

&lt;p&gt;Most people write prompts like they're texting a friend. "Hey can you look at this code?" That's why the answers are mid.&lt;/p&gt;

&lt;p&gt;Fifteen seconds of prompt structure is the difference between a useful answer and a twenty-minute detour.&lt;/p&gt;




&lt;p&gt;I've been collecting these for about six months now. These ten are the ones I reach for daily, but I've got around 40 more that cover architecture decisions, performance profiling, caching strategies, threat modeling, that kind of thing. I cleaned them all up and &lt;a href="https://maxtendies.gumroad.com/l/claude-code-prompt-pack" rel="noopener noreferrer"&gt;put them on Gumroad&lt;/a&gt; if you want the full set. Or don't. These ten will get you pretty far on their own.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
