<?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: Md Ayan bag</title>
    <description>The latest articles on DEV Community by Md Ayan bag (@md_ayanbag_03a42545b17ac).</description>
    <link>https://dev.to/md_ayanbag_03a42545b17ac</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4143492%2Fbddf5d60-b010-4296-b3ed-f130b4fa3c68.png</url>
      <title>DEV Community: Md Ayan bag</title>
      <link>https://dev.to/md_ayanbag_03a42545b17ac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/md_ayanbag_03a42545b17ac"/>
    <language>en</language>
    <item>
      <title>Claude Code /compact vs /clear: When to Use Each (With My Numbers)</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Sat, 24 Oct 2026 18:38:07 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/claude-code-compact-vs-clear-when-to-use-each-with-my-numbers-38ln</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/claude-code-compact-vs-clear-when-to-use-each-with-my-numbers-38ln</guid>
      <description>&lt;p&gt;Last week Claude Code compacted my conversations 67 times. I didn't type /compact once. Every one of them was auto-compact, firing in the middle of work.&lt;/p&gt;

&lt;p&gt;I only noticed because I wrote a small script that reads Claude Code's session logs on my Mac. The median compaction happened at &lt;strong&gt;969,037 tokens&lt;/strong&gt;. The turn right after it still carried about 106,000.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I used Claude Code to help write the log script and to draft this post. The numbers come from my own machine, from 237 sessions over 7 days.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've wondered whether to run /compact or /clear, or why Claude Code keeps compacting your conversation, this is what I learned and the rule I follow now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What /compact actually does
&lt;/h2&gt;

&lt;p&gt;/compact asks Claude to summarize the conversation so far, then replaces the history with that summary. You stay in the same session.&lt;/p&gt;

&lt;p&gt;The summary is written by the model, not by you. It usually keeps the goal and the recent steps. It often drops the small stuff: the exact error message from an hour ago, why you rejected an approach, which test was flaky.&lt;/p&gt;

&lt;p&gt;You can steer it. Anything you type after the command becomes instructions for the summary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/compact keep the failing test names, the API shape we agreed on, and the files we touched
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one habit fixed most of my "why did it forget that" moments.&lt;/p&gt;

&lt;h2&gt;
  
  
  What /clear actually does
&lt;/h2&gt;

&lt;p&gt;/clear throws the conversation away. The next message starts a fresh context with your CLAUDE.md, settings and tools loaded again, and nothing else.&lt;/p&gt;

&lt;p&gt;It's the cheapest thing you can do for your usage limit, because Claude Code re-sends the whole context on every turn. A clean session at 20k tokens costs a fraction per turn of one dragging 200k behind it.&lt;/p&gt;

&lt;p&gt;The catch is obvious. If you're halfway through something, /clear forgets it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Code compact vs clear: the rule I use
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Finished the task? /clear.&lt;/strong&gt; Don't compact a conversation you no longer need. The summary of yesterday's bug is pure overhead for today's feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mid-task, first time it gets heavy? /compact with instructions.&lt;/strong&gt; One compaction with a clear "keep X, Y, Z" is fine.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mid-task, long work, or it already compacted once? Write a handoff, then /clear.&lt;/strong&gt; More on that below.&lt;/p&gt;

&lt;p&gt;[[IMG1]]&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it keeps compacting too often
&lt;/h2&gt;

&lt;p&gt;The search "claude code compacting conversation too often" is real, and I was the case study. Three things caused it for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Long sessions.&lt;/strong&gt; 139 of my 237 sessions ran 80 or more model turns. Those sessions produced 99% of all the context I sent that week. Auto-compact is a symptom of a session that should have ended earlier.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Big tool output.&lt;/strong&gt; A full test run, a lockfile read, a build log. Each one lands in context and stays there. Pipe noisy commands through &lt;code&gt;tail -n 40&lt;/code&gt; and ask Claude to read line ranges instead of whole files.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A heavy baseline.&lt;/strong&gt; CLAUDE.md files, MCP tool lists and skill descriptions load before you type anything. Run &lt;code&gt;/context&lt;/code&gt; to see what your session is carrying, and you'll know how much room you really have.&lt;/p&gt;

&lt;p&gt;Compacting 55 different sessions in a week wasn't bad luck. It was me never closing the window.&lt;/p&gt;

&lt;h2&gt;
  
  
  The handoff: the option most people skip
&lt;/h2&gt;

&lt;p&gt;A handoff is a short note Claude writes to a file before you clear. You then start the new session from the note. It works like /compact, except you can read it, fix it, and keep it.&lt;/p&gt;

&lt;p&gt;Here's the prompt I use. Paste it when context is above about 60%:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Write .claude/HANDOFF.md, under 40 lines, facts only:
&lt;span class="gu"&gt;## Goal (1-2 lines)&lt;/span&gt;
&lt;span class="gu"&gt;## Done (what is finished and verified)&lt;/span&gt;
&lt;span class="gu"&gt;## Next (the exact next 1-3 steps)&lt;/span&gt;
&lt;span class="gu"&gt;## Key files (paths and line numbers, no contents)&lt;/span&gt;
&lt;span class="gu"&gt;## Gotchas (decisions made, what failed and why)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run /clear and start with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Read .claude/HANDOFF.md and continue from "Next".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new session starts at your baseline plus about a thousand tokens. Compare that with the 106k my sessions carried after an auto-compact.&lt;/p&gt;

&lt;p&gt;The note is also better than a summary in one way that matters: it records &lt;em&gt;why&lt;/em&gt;. "We tried caching in Redis, it broke the tests, don't do it again" survives a handoff. It rarely survives a compaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common mistakes (I made all of them)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Compacting twice in a row.&lt;/strong&gt; A summary of a summary loses detail every time. If you're about to compact a second time, write a handoff instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Clearing mid-debug.&lt;/strong&gt; You lose the stack traces and the list of things you already ruled out. Write the handoff first, even a five-line one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Turning auto-compact off and forgetting.&lt;/strong&gt; You can disable it in &lt;code&gt;/config&lt;/code&gt;. Then you hit the context limit and the session just stops being useful. If you turn it off, watch your context percentage instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not looking at the number.&lt;/strong&gt; Put context usage in your status line. Claude Code passes the percentage to status line scripts, so you see 60% coming instead of meeting a compaction at 95%.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to check your own numbers
&lt;/h2&gt;

&lt;p&gt;Claude Code stores every session as a JSONL file under &lt;code&gt;~/.claude/projects/&lt;/code&gt;. Each compaction is logged as a line with &lt;code&gt;"subtype": "compact_boundary"&lt;/code&gt;, and it records how many tokens the session had before it.&lt;/p&gt;

&lt;p&gt;This one-liner counts your compactions:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; &lt;span class="s1"&gt;'"compact_boundary"'&lt;/span&gt; ~/.claude/projects/&lt;span class="k"&gt;*&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.jsonl | &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;If that number is high, your sessions are too long. That's the whole diagnosis.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I use now
&lt;/h2&gt;

&lt;p&gt;I got tired of pasting the handoff prompt and writing status line scripts on every machine, so I packaged my setup. &lt;strong&gt;Full disclosure: I built it and sell it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It includes a /handoff command that writes the note and reloads it after /clear, a status line that warns at 60% context, a hook that blocks junk file reads, and an audit script that prints numbers like the ones in this post. Everything above works without it. If you want it set up in two minutes, it's the &lt;a href="https://mdayan.gumroad.com/l/claude-limit-saver?utm_source=devto&amp;amp;utm_campaign=compact-vs-clear" rel="noopener noreferrer"&gt;Claude Code Limit Saver Kit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;/clear when the task is done. /compact once, with instructions, when it isn't. After that, write a handoff and start clean.&lt;/p&gt;

&lt;p&gt;Run the grep above and tell me your count in the comments. Mine was 89 all-time, 67 of them last week, and I'd like to know if I'm the worst.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@mdayan8/claude-code-compact-vs-clear-when-to-use-each-with-my-numbers-2ad3a24d408f" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Stopped Hitting Claude Code Usage Limits</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Sat, 24 Oct 2026 18:37:04 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/how-i-stopped-hitting-claude-code-usage-limits-3cfc</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/how-i-stopped-hitting-claude-code-usage-limits-3cfc</guid>
      <description>&lt;p&gt;"You've hit your usage limit." I saw that message three times in one week, every time in the middle of a task, with half-written code open in front of me.&lt;/p&gt;

&lt;p&gt;So I stopped guessing and measured. I wrote a small script that reads Claude Code's own session logs on my Mac. The result was embarrassing: &lt;strong&gt;my median session peaked at 252,696 tokens.&lt;/strong&gt; 160 of my 230 sessions that week went past 150k. My largest hit 967k.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: I used Claude Code to help build the audit script and draft this post. The numbers and screenshots are from my own machine.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Here is what was actually eating my limit, and the nine fixes that work. Most of them are free and take five minutes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Claude Code usage limits drain so fast
&lt;/h2&gt;

&lt;p&gt;Claude Code re-sends the whole conversation to the model on every turn.&lt;/p&gt;

&lt;p&gt;Your CLAUDE.md, every MCP tool definition, every file it opened, every test log it printed, every message you sent. Turn 80 pays for turns 1 to 79 again.&lt;/p&gt;

&lt;p&gt;So your limit isn't eaten by your questions. It's eaten by &lt;strong&gt;context size times number of turns&lt;/strong&gt;. A session sitting at 200k tokens carries ten times more per turn than one at 20k.&lt;/p&gt;

&lt;p&gt;Every fix below shrinks one of those two numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Put the limit where you can see it
&lt;/h2&gt;

&lt;p&gt;You can't manage what you only see when it's too late. Claude Code's status line receives your context percentage and, on Pro and Max, your 5-hour and weekly limit usage. Most people never turn it on.&lt;/p&gt;

&lt;p&gt;Save this as &lt;code&gt;~/.claude/gauge.py&lt;/code&gt;:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;
&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;ctx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;context_window&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;used_percentage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;rl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rate_limits&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="n"&gt;five&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;five_hour&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;used_percentage&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;week&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;seven_day&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;used_percentage&lt;/span&gt;&lt;span class="sh"&gt;"&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;ctx &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% | 5h &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;five&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;% | week &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;week&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&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;Then add this to &lt;code&gt;~/.claude/settings.json&lt;/code&gt; and restart:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"statusLine"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python3 ~/.claude/gauge.py"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you see the problem building instead of meeting it at 100%.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. One task per session, then /clear
&lt;/h2&gt;

&lt;p&gt;This was my biggest leak. 137 of my sessions ran 80+ model turns, because I kept working in the same window all afternoon.&lt;/p&gt;

&lt;p&gt;Finished a feature? Run &lt;code&gt;/clear&lt;/code&gt; before the next one. Mid-task and context is above 60%? Ask Claude to write a short handoff note to a file (goal, what's done, next steps, key files), then &lt;code&gt;/clear&lt;/code&gt; and point the new session at that note.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;/compact vs /clear:&lt;/strong&gt; compacting keeps a summary in the same session. It's fine once. Do it four times and you're dragging a blurry, still-heavy history around. A 40-line handoff plus a clear is cheaper, and you decide what survives.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Stop Claude from reading junk files
&lt;/h2&gt;

&lt;p&gt;One accidental read of a lockfile or a minified bundle can add tens of thousands of tokens that ride along for the rest of the session.&lt;/p&gt;

&lt;p&gt;Add deny rules to your project's &lt;code&gt;.claude/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./node_modules/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./dist/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./build/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(./package-lock.json)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./pnpm-lock.yaml)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Read(./**/*.min.js)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And name exact places in your prompts. "Fix the null check in src/lib/auth.ts around getUser" costs a fraction of "fix the login bug".&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Keep CLAUDE.md under about 1,000 tokens
&lt;/h2&gt;

&lt;p&gt;CLAUDE.md is loaded on every single turn. So is every CLAUDE.md in parent folders, which surprised me: mine was quietly pulling in a 1,800-token file from my home folder.&lt;/p&gt;

&lt;p&gt;Keep it to the stack, the commands, where things live, and the few gotchas that bit you before. Move long docs into a docs/ folder and reference them by path. Claude opens them only when it needs them.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Turn off MCP servers you aren't using
&lt;/h2&gt;

&lt;p&gt;Every MCP server injects its full tool list into context, on every turn. Run &lt;code&gt;/mcp&lt;/code&gt; and disable what this project doesn't need. Three focused servers beat twelve "just in case" ones. The same goes for plugins you installed once and forgot.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Plan before Claude writes code
&lt;/h2&gt;

&lt;p&gt;Press Shift+Tab for plan mode on anything non-trivial. Fixing a wrong approach after 40k tokens of code costs far more than reading a 2k-token plan and saying "no, do it this way".&lt;/p&gt;

&lt;p&gt;Batch your asks, too. One message with three clear changes beats three back-and-forth turns that each re-send everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Use a cheaper model for mechanical work
&lt;/h2&gt;

&lt;p&gt;Use your heaviest model for design and hard bugs, and switch with &lt;code&gt;/model&lt;/code&gt; for renames, tests and docs.&lt;/p&gt;

&lt;p&gt;Subagents can run on a lighter model too. Add &lt;code&gt;model: haiku&lt;/code&gt; to an agent's frontmatter in &lt;code&gt;~/.claude/agents/&lt;/code&gt; and send it the searching and boilerplate. The noise stays out of your main context.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Make Claude write less code
&lt;/h2&gt;

&lt;p&gt;Ponytail is a free, MIT-licensed skill that pushes Claude to reuse what exists and skip speculative abstractions, without dropping validation or security. Its published benchmark on real Claude Code tasks shows about 54% less code and about 22% fewer tokens. I've had it on for weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Measure every week
&lt;/h2&gt;

&lt;p&gt;Your feeling about usage is wrong. Mine was. Claude Code keeps every session as a JSONL file under &lt;code&gt;~/.claude/projects/&lt;/code&gt;, with token counts on every response, so you can measure exactly how big your sessions get.&lt;/p&gt;

&lt;p&gt;That's what my audit script does. It prints what your setup costs before you type a word, then your real session sizes for the week.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I packaged up
&lt;/h2&gt;

&lt;p&gt;Doing all of this by hand on every machine got old, so I turned my setup into a kit. &lt;strong&gt;Full disclosure: I built it and sell it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It has the full limit gauge with colours and reset timers, a hook that blocks junk reads automatically, a /handoff command that saves the note and reloads it after /clear, the audit script, a local cost dashboard, a lean CLAUDE.md template and a 3-page playbook. It installs with one command and uninstalls with one command.&lt;/p&gt;

&lt;p&gt;Everything in this article works without it. If you'd rather have it set up in two minutes, it's here: &lt;a href="https://mdayan.gumroad.com/l/claude-limit-saver?utm_source=devto&amp;amp;utm_campaign=usage-limits" rel="noopener noreferrer"&gt;Claude Code Limit Saver Kit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;Your limit isn't spent on your questions. It's spent on carrying context. Watch the gauge, clear between tasks, block junk reads, keep CLAUDE.md lean, and measure.&lt;/p&gt;

&lt;p&gt;What's your worst session? Run the numbers and tell me in the comments. I read every one.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/@mdayan8/how-i-stopped-hitting-claude-code-usage-limits-9778e360873f" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Just Got a 256 GB MacBook? Set It Up So It Doesn’t Fill Up in a Year</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:04:33 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/just-got-a-256-gb-macbook-set-it-up-so-it-doesnt-fill-up-in-a-year-n78</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/just-got-a-256-gb-macbook-set-it-up-so-it-doesnt-fill-up-in-a-year-n78</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw597e598391ul5d1j02a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fw597e598391ul5d1j02a.png" alt="Just got a 256 GB MacBook? How to set it up so it doesn't fill up" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The MacBook Neo starts at $599 with 256 GB. The base MacBook Air has the same.&lt;/p&gt;

&lt;p&gt;That’s plenty on day one. macOS takes about 25 GB, and you look at the 200-something GB left and think you’ll never fill it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(I wrote this with help from an AI assistant. Every number is measured on my own Mac.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I thought that too. My MacBook Air has a 245 GB disk. Today it has &lt;strong&gt;4.13 GB free&lt;/strong&gt;, and the biggest thing on it is not my photos or my projects. It’s a grey bar called “System Data” at &lt;strong&gt;142.69 GB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post is the setup I’d do on a new 256 GB Mac, knowing what I know now. Most of it is free and built into macOS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually fills a small Mac
&lt;/h2&gt;

&lt;p&gt;Not what you’d guess. I measured mine folder by folder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhjh3wurk9kwaeisourfr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhjh3wurk9kwaeisourfr.png" alt="What filled my 245 GB Mac, measured with Tidy Mac" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App data, 39.31 GB.&lt;/strong&gt; The private folders apps keep in &lt;code&gt;~/Library/Application Support&lt;/code&gt;: databases, offline content, downloaded models. It grows quietly, and it stays behind when you delete the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Library and Homebrew, 31.88 GB.&lt;/strong&gt; That one’s on me. I’m a developer. If you don’t install command-line tools, yours will be small.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System caches and temp files, 10.93 GB.&lt;/strong&gt; macOS manages these. Leave them alone.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer and AI caches, 7.8 GB.&lt;/strong&gt; Package caches and AI models in hidden dot-folders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caches, 4.11 GB.&lt;/strong&gt; Safe to clear, but they regrow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The lesson: on a small disk, the problem isn’t one big file. It’s a hundred apps each keeping “just a little” data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Ten minutes in System Settings, before you install anything
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feum192pea0rgejpiowwl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feum192pea0rgejpiowwl.png" alt="The 10-minute setup for a new 256 GB Mac" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;General › Storage.&lt;/strong&gt; Turn on Optimize Storage (removes TV shows you’ve watched) and Empty Trash Automatically (deletes Trash items after 30 days).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Photos › Settings › iCloud › Optimize Mac Storage.&lt;/strong&gt; If you have a big photo library and an iCloud plan, this alone can save tens of gigabytes. Full photos stay in iCloud and download when you open them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iCloud Drive › Optimize Mac Storage&lt;/strong&gt; (in System Settings › your name › iCloud). Old files you haven’t opened stay in the cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Messages › Settings › General › Keep messages: 1 year.&lt;/strong&gt; Years of videos people sent you add up. Only do this if you’re fine losing old threads, or if Messages in iCloud has them.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 2: Don’t back up your iPhone to the Mac
&lt;/h2&gt;

&lt;p&gt;One iPhone backup can be bigger than everything else in your home folder. If you plug your phone in and Finder offers to back it up “to this Mac”, say no and use iCloud Backup instead.&lt;/p&gt;

&lt;p&gt;Already have one? Open Finder, click your iPhone in the sidebar, then &lt;strong&gt;Manage Backups&lt;/strong&gt; to see the size and delete old ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Install fewer apps, and uninstall properly
&lt;/h2&gt;

&lt;p&gt;Every app you try for a day leaves files in your Library folder after you drag it to the Trash. Settings, caches, sometimes gigabytes of downloaded content.&lt;/p&gt;

&lt;p&gt;Two rules I’d follow on a new Mac:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Try apps in the first week, then decide.&lt;/strong&gt; Keep what you used. Delete the rest while you still remember what you installed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remove the leftovers too.&lt;/strong&gt; Look in &lt;code&gt;~/Library/Application Support&lt;/code&gt;, &lt;code&gt;~/Library/Caches&lt;/code&gt; and &lt;code&gt;~/Library/Containers&lt;/code&gt; for folders named after the app. Or use an uninstaller that does it for you.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To see the biggest folders yourself (this deletes nothing):&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;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/&lt;span class="k"&gt;*&lt;/span&gt; ~/.[!.]&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-15&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One catch: &lt;code&gt;du&lt;/code&gt; counts APFS clones at full size, so a folder can look far bigger than it really is. Check free space before and after you delete anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4: Put big files somewhere else
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Videos and old projects:&lt;/strong&gt; an external SSD. A 1 TB one often costs less than Apple’s storage upgrade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Downloads:&lt;/strong&gt; sort by size once a month. Installers (.dmg, .pkg) are safe to delete after you’ve installed the app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time Machine:&lt;/strong&gt; plug in a drive in week one. It backs everything up, and it means you can delete with less fear.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Step 5: Watch the number, not the warning
&lt;/h2&gt;

&lt;p&gt;macOS warns you when the disk is almost full. By then you have a few gigabytes and no idea what changed.&lt;/p&gt;

&lt;p&gt;Write down your free space today. Check it once a month. If it drops 20 GB in a week, something changed that week, and you’ll know where to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I use to keep an eye on it
&lt;/h2&gt;

&lt;p&gt;Doing step 5 by hand got old, so I built &lt;a href="https://tidymac.space/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=256gb-setup" rel="noopener noreferrer"&gt;Tidy Mac&lt;/a&gt;. Full disclosure: it’s my app.&lt;/p&gt;

&lt;p&gt;It shows your free space in the menu bar, with a 7-day and 30-day trend. Its Home screen splits “System Data” into named parts, like the chart above. And Clean sorts caches into “Safe to clear”, “Worth a look” and “Kept”, so you don’t have to guess:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fliigast5llrq6xv4yyqk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fliigast5llrq6xv4yyqk.png" alt="Tidy Mac's Clean screen: only items that rebuild themselves are ticked" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Everything goes to the Trash first, and History has Undo. It’s free to download and look around, and the first 500 MB of cleaning is free. After that it’s Pro, $9.99 once for 2 Macs. No account, and your files never leave your Mac.&lt;/p&gt;

&lt;p&gt;You don’t need it to follow this checklist. Steps 1 to 4 are free and do most of the work.&lt;/p&gt;

&lt;p&gt;How much free space does your Mac have right now? Tell me in the comments, with the size of the disk. I’m curious how fast everyone else’s fills up.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>His Mac Cleaner Freed 3 GB. The Other 40 GB Was Hiding in “System Data”.</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Thu, 22 Oct 2026 04:50:29 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/his-mac-cleaner-freed-3-gb-the-other-40-gb-was-hiding-in-system-data-8if</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/his-mac-cleaner-freed-3-gb-the-other-40-gb-was-hiding-in-system-data-8if</guid>
      <description>&lt;p&gt;A developer friend's MacBook Air kept saying the disk was almost full. He was already paying for CleanMyMac. He ran its clean-up, and it freed about &lt;strong&gt;3 GB&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Then he looked inside the grey bar every Mac owner has learned to hate: &lt;strong&gt;System Data&lt;/strong&gt;. About &lt;strong&gt;40 GB&lt;/strong&gt; came back from inside it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkdtv7e058mcdb98sag4g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkdtv7e058mcdb98sag4g.png" alt="Same MacBook Air, two clean-ups: about 3 GB vs about 40 GB" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The cleaner wasn't broken. It was looking in the wrong places. On a developer's Mac, most of the space isn't caches or logs. It's build files, simulators, package caches and old installers, all filed under System Data.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own Mac in one command
&lt;/h2&gt;

&lt;p&gt;It only measures; it deletes nothing.&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;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Developer ~/Library/Caches ~/.npm ~/Library/Containers/com.docker.docker ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/MobileSync/Backup 2&amp;gt;/dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A missing line means the folder doesn't exist (or Terminal needs Full Disk Access). Anything over 5 GB is worth a look.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Xcode build files
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;~/Library/Developer/Xcode/DerivedData&lt;/code&gt; rebuilds itself on the next build. Quit Xcode, then empty it.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Simulators and device support
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcrun simctl delete unavailable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Remove old simulator runtimes in &lt;strong&gt;Xcode → Settings → Components&lt;/strong&gt; (Platforms in older Xcode).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/Library/Developer/Xcode/iOS DeviceSupport&lt;/code&gt; keeps a folder per iOS version per device. Delete the old ones; Xcode copies them again when needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3. Package caches
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm cache clean &lt;span class="nt"&gt;--force&lt;/span&gt;
brew cleanup &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;all
pip cache purge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Old &lt;code&gt;node_modules&lt;/code&gt; in projects you no longer touch add up too. &lt;code&gt;npm install&lt;/code&gt; brings them back.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Old installers
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find ~/Downloads &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 1 &lt;span class="se"&gt;\(&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.dmg"&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.pkg"&lt;/span&gt; &lt;span class="se"&gt;\)&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; +
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus any "Install macOS" app in Applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not on his Mac, but check yours
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;iPhone backups:&lt;/strong&gt; Finder → your iPhone → Manage Backups.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker:&lt;/strong&gt; &lt;code&gt;docker system df&lt;/code&gt;, then &lt;code&gt;docker system prune&lt;/code&gt; for what's unused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time Machine snapshots:&lt;/strong&gt; &lt;code&gt;tmutil listlocalsnapshots /&lt;/code&gt;. Leave them; macOS removes them when it needs space.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1etwor4n0txsmoy7qzqt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F1etwor4n0txsmoy7qzqt.png" alt="Where the space was, and what each tool costs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What not to delete
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Anything under &lt;code&gt;/System&lt;/code&gt;, or whole folders in &lt;code&gt;~/Library&lt;/code&gt;. Delete specific items, never the parent.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/Library/Mobile Documents&lt;/code&gt; (that's iCloud Drive).&lt;/li&gt;
&lt;li&gt;Anything inside the Photos library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When in doubt, move it to the Trash and empty it a week later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is a cleaner worth paying for?
&lt;/h2&gt;

&lt;p&gt;If you don't write code and your photos live in iCloud, probably not: turn on the recommendations in System Settings → General → Storage.&lt;/p&gt;

&lt;p&gt;If System Data is huge and you'd rather not hunt folder by folder, an app that names every part of it saves the time.&lt;/p&gt;

&lt;p&gt;Full disclosure: I build &lt;a href="https://tidymac.space/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=cmm-3gb-40gb" rel="noopener noreferrer"&gt;Tidy Mac&lt;/a&gt;, the app my friend used. The download is free and shows everything filling your Mac; cleaning past the first 500 MB is $9.99 once, for two Macs. Full case study: &lt;a href="https://tidymac.space/stories/cleanmymac-3gb-tidymac-40gb/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=cmm-3gb-40gb" rel="noopener noreferrer"&gt;CleanMyMac freed 3 GB, Tidy Mac found 40 GB&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How big is your System Data? Tell me in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written with help from an AI assistant. The 3 GB and 40 GB figures are from my friend's Mac.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>productivity</category>
      <category>ios</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build a chat-style intake flow from your server with six curl calls</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Wed, 21 Oct 2026 03:30:00 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/build-a-chat-style-intake-flow-from-your-server-with-six-curl-calls-590n</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/build-a-chat-style-intake-flow-from-your-server-with-six-curl-calls-590n</guid>
      <description>&lt;p&gt;Every "conversational" intake form I have built from scratch ended up the same way: a state machine in a route handler, a &lt;code&gt;switch&lt;/code&gt; on the current question, hand-written validation for each field, and a branching table nobody wanted to touch after launch. Then product asks for "ask a follow-up if the answer is vague", and now there is a model call in the middle of the state machine too.&lt;/p&gt;

&lt;p&gt;The alternative is to let a form engine drive the conversation and keep only the UI. This tutorial walks through chatform's headless API: you open a session on a published form, post answers, and the engine tells you what to ask next, what was rejected and when the conversation is complete. Everything below comes from the docs at chatform.in/docs; I have not invented any response fields.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you need
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A published chatform form. Every question has a &lt;code&gt;ref&lt;/code&gt; like &lt;code&gt;q_email&lt;/code&gt;; that is how you address it.&lt;/li&gt;
&lt;li&gt;A secret key from Settings → API keys. It starts with &lt;code&gt;sk_live_&lt;/code&gt; (or &lt;code&gt;sk_test_&lt;/code&gt;, which writes rows flagged as test data and never fires webhooks). Keep it on your server.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;jq&lt;/code&gt;. The Node snippet at the end uses the built-in &lt;code&gt;fetch&lt;/code&gt;, so Node 18 or newer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Export two variables so the commands below paste cleanly:&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;export &lt;/span&gt;&lt;span class="nv"&gt;CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sk_test_...
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;FORM_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1. Check the key
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.chatform.in/v1/me &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This returns the organization the key belongs to, its scopes, and what is left of the plan. If this call fails, nothing else will work, so start here. A &lt;code&gt;401&lt;/code&gt; with code &lt;code&gt;invalid_api_key&lt;/code&gt; means the key is not one the API knows. You can also send the key as &lt;code&gt;authorization: Bearer sk_...&lt;/code&gt;, but never in a query string; &lt;code&gt;/v1&lt;/code&gt; refuses keys in URLs.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Read the form's questions
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.chatform.in/v1/forms/&lt;span class="nv"&gt;$FORM_ID&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;ref&lt;/code&gt; of each block. If the form is not live you get &lt;code&gt;not_published&lt;/code&gt;; the draft is readable with &lt;code&gt;?view=document&lt;/code&gt;, but it takes no responses until you publish it.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Open a session
&lt;/h2&gt;

&lt;p&gt;A session is a conversation on a published form, driven by you instead of the hosted page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.chatform.in/v1/forms/&lt;span class="nv"&gt;$FORM_ID&lt;/span&gt;/sessions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"hiddenFields": {"plan": "trial"}}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sessionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"chs_…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"respondentToken"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"expiresAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1788592149473&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"streamUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/v1/sessions/chs_…/events"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"greeting"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Hi! Let's get started."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"question"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"q_email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"What's your email?"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The body also accepts &lt;code&gt;externalId&lt;/code&gt; (your own id for the respondent, up to 200 characters) and &lt;code&gt;expiresIn&lt;/code&gt; in seconds, between 300 and 2,592,000. Save &lt;code&gt;sessionId&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SESSION_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;chs_...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth knowing. The close date, response ceiling and submission cap of the hosted form all apply here. The form password and captcha do not, because an API key is stronger proof than a password and there is no browser to solve a captcha.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;respondentToken&lt;/code&gt; is the piece that makes a browser front end safe. It is scoped to this one session and expires, so you hand it to the page and keep the secret key on the server. If a request carrying a secret key arrives with an &lt;code&gt;Origin&lt;/code&gt; header, the API refuses it with &lt;code&gt;secret_key_in_browser&lt;/code&gt; and tells you to rotate the key.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Answer the question
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.chatform.in/v1/sessions/&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;/messages &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"type": "structured", "ref": "q_email", "value": "maya@northwind.co"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are two message shapes. &lt;code&gt;structured&lt;/code&gt; is an answer to a specific question, what a form control would produce. &lt;code&gt;text&lt;/code&gt; is free text the engine interprets against whatever it just asked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"text"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"we're about a dozen people"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Either way the reply is the whole turn:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"assistantMessages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Got it. And what's your role?"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"question"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ref"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"q_role"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"validation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"complete"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"awaitingSubmit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"events"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"seq"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"answer_recorded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"…"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"…"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"answers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"q_email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"maya@northwind.co"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your loop is: render &lt;code&gt;assistantMessages&lt;/code&gt;, render &lt;code&gt;question&lt;/code&gt;, collect an answer, post it, repeat. A rejected answer is not an HTTP error. You still get &lt;code&gt;accepted: true&lt;/code&gt;, plus a &lt;code&gt;validation&lt;/code&gt; object with &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;code&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt;, and the same &lt;code&gt;question&lt;/code&gt; again. The message is the same text a respondent would see, so you can show it as is.&lt;/p&gt;

&lt;p&gt;When the engine reaches an ending, &lt;code&gt;question&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;ending&lt;/code&gt; carries &lt;code&gt;ref&lt;/code&gt;, &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;bodyMd&lt;/code&gt; and a &lt;code&gt;kind&lt;/code&gt; of &lt;code&gt;success&lt;/code&gt; or &lt;code&gt;screen_out&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Submit
&lt;/h2&gt;

&lt;p&gt;Forms default to showing a review step before finishing. When &lt;code&gt;awaitingSubmit&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, the session is waiting for exactly one thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://api.chatform.in/v1/sessions/&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;/actions &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"content-type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"action": "submit"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without this, such a form can never complete. The same endpoint takes &lt;code&gt;skip&lt;/code&gt;, &lt;code&gt;stop&lt;/code&gt;, &lt;code&gt;restart&lt;/code&gt; and &lt;code&gt;edit&lt;/code&gt; (with a &lt;code&gt;ref&lt;/code&gt;), so back buttons and "start over" links are one call each.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Read state, or catch up after a drop
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://api.chatform.in/v1/sessions/&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Session state includes &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;currentRef&lt;/code&gt;, &lt;code&gt;answers&lt;/code&gt;, &lt;code&gt;awaitingSubmit&lt;/code&gt;, &lt;code&gt;completedAt&lt;/code&gt; and a &lt;code&gt;summary&lt;/code&gt; array of &lt;code&gt;{ ref, title, display }&lt;/code&gt;, which is handy for a review screen.&lt;/p&gt;

&lt;p&gt;The events endpoint has two modes. With &lt;code&gt;accept: text/event-stream&lt;/code&gt; it is a server-sent event stream, and every event carries a &lt;code&gt;seq&lt;/code&gt;. Without that header it returns a JSON page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.chatform.in/v1/sessions/&lt;/span&gt;&lt;span class="nv"&gt;$SESSION_ID&lt;/span&gt;&lt;span class="s2"&gt;/events?since=12"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"x-api-key: &lt;/span&gt;&lt;span class="nv"&gt;$CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That returns &lt;code&gt;{ events, latest_seq, has_more }&lt;/code&gt;. Keep the highest &lt;code&gt;seq&lt;/code&gt; you have seen and you can resume exactly where you left off.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same loop in Node
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BASE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.chatform.in/v1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;x-api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CHATFORM_SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;content-type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;openSession&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forms/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;formId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/sessions`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// { sessionId, respondentToken, question, ... }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;BASE&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/sessions/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;sessionId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/messages`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;structured&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// { accepted, validation, question, complete, awaitingSubmit, ... }&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hand &lt;code&gt;sessionId&lt;/code&gt; and &lt;code&gt;respondentToken&lt;/code&gt; to the page; keep everything else behind this module.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errors you will actually hit
&lt;/h2&gt;

&lt;p&gt;Every error uses one envelope: &lt;code&gt;{ "error": { "code", "message", "issues", "request_id" } }&lt;/code&gt;. The &lt;code&gt;request_id&lt;/code&gt; is also the &lt;code&gt;X-Request-Id&lt;/code&gt; header on every response, so log it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;401&lt;/strong&gt; &lt;code&gt;unauthorized&lt;/code&gt;, &lt;code&gt;invalid_api_key&lt;/code&gt;, &lt;code&gt;api_key_disabled&lt;/code&gt;, &lt;code&gt;api_key_expired&lt;/code&gt;. Wording is identical on purpose; read the code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403&lt;/strong&gt; &lt;code&gt;secret_key_in_browser&lt;/code&gt;, &lt;code&gt;origin_not_allowed&lt;/code&gt;, &lt;code&gt;insufficient_scope&lt;/code&gt;. A key with only &lt;code&gt;form:read&lt;/code&gt; cannot start a session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;403 on opening a session&lt;/strong&gt; means the form is closed, capped, or over its response ceiling.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;404&lt;/strong&gt; is also what you get for a form that belongs to another tenant. That is deliberate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;400&lt;/strong&gt; &lt;code&gt;invalid_request&lt;/code&gt; with &lt;code&gt;issues[]&lt;/code&gt; naming each bad field, or &lt;code&gt;malformed_json&lt;/code&gt; for a JSON content type with a body that is not JSON. An empty body counts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;202&lt;/strong&gt; on a message is not a failure. An interview turn may involve a model call. Past the deadline you get &lt;code&gt;202&lt;/code&gt; with &lt;code&gt;sinceSeq&lt;/code&gt; and a &lt;code&gt;pollUrl&lt;/code&gt;. Pass &lt;code&gt;?deadlineMs=&lt;/code&gt; to pick your own deadline, up to 25 seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;429&lt;/strong&gt; &lt;code&gt;rate_limited&lt;/code&gt; with &lt;code&gt;Retry-After&lt;/code&gt; in seconds. Retry it. &lt;strong&gt;402&lt;/strong&gt; &lt;code&gt;limit_reached&lt;/code&gt; means the monthly quota is spent and retrying is futile.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where to go next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET /v1/blocks&lt;/code&gt; returns every question type with its config schema, the shape you receive and the shape you send. Build your renderer against that and new block types will not surprise you.&lt;/li&gt;
&lt;li&gt;File questions are answered by uploading: register an intent, &lt;code&gt;PUT&lt;/code&gt; the bytes, confirm.&lt;/li&gt;
&lt;li&gt;Forms with sign-in emit &lt;code&gt;auth_required&lt;/code&gt; and accept Google, phone or email proof on dedicated routes.&lt;/li&gt;
&lt;li&gt;If you would rather not write the HTTP, there is a typed JavaScript client and React bindings.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Disclosure:&lt;/strong&gt; I work on chatform. This post was drafted with AI assistance and checked against the docs by hand. The full headless reference, including streaming, verified answers and payments, is at &lt;a href="https://chatform.in/docs/headless?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=headless-api-tutorial" rel="noopener noreferrer"&gt;chatform.in/docs/headless&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>node</category>
    </item>
    <item>
      <title>81% of People Abandon Forms. Here's How I Get Them Back Without Rebuilding Anything.</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Tue, 20 Oct 2026 17:00:00 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/81-of-people-abandon-forms-heres-how-i-get-them-back-without-rebuilding-anything-1a62</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/81-of-people-abandon-forms-heres-how-i-get-them-back-without-rebuilding-anything-1a62</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkg57xojfhgww481hjs6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkg57xojfhgww481hjs6.jpg" alt="chatform, a conversational form, on a laptop and a phone" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most people who open your form will not finish it. In The Manifest's survey, 81% said they had abandoned at least one online form recently. Across the industry the number that gets quoted is around 67% of starts never reaching submit.&lt;/p&gt;

&lt;p&gt;I work on chatform, a form builder, so I see these numbers every day across other people's forms. That's my stake, and I'll say when I'm talking about my own product. I used an AI assistant to help outline this and wrote and checked the rest myself.&lt;/p&gt;

&lt;p&gt;What follows is the order I'd fix things in, starting with changes that cost nothing and work in any form tool, including Google Forms. The product comes last, and you won't need it for most of this.&lt;/p&gt;

&lt;h2&gt;
  
  
  First, find the question where they leave
&lt;/h2&gt;

&lt;p&gt;Abandonment is not a single number. It's a cliff at one question. Nine times out of ten it's a field asking for more than the person wants to give at that point: a phone number, a budget, a home address for a digital product.&lt;/p&gt;

&lt;p&gt;Most builders show a drop-off per question somewhere in their analytics. If yours doesn't, split the form into two pages and compare submissions on page one against page two. That's a crude funnel and it's enough to find the cliff.&lt;/p&gt;

&lt;p&gt;Once you know the question, you have three options: make it optional, move it later, or delete it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make the phone number optional
&lt;/h2&gt;

&lt;p&gt;I know sales wants it. A required phone field is the single most common reason people quit a form. Make it optional and ask for it again in the thank-you email. You'll lose a few numbers and gain a lot of completed forms, and a completed form with an email is a lead you can still call once they reply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Say how long it takes, and show progress
&lt;/h2&gt;

&lt;p&gt;In the Loqate survey, people said they often don't know whether finishing a form is worth the effort. Tell them: "Eight questions, about three minutes." Multi-step forms without a progress indicator abandon about 20% more than forms that show where you are. Both fixes are a sentence and a checkbox in most tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the easy questions first
&lt;/h2&gt;

&lt;p&gt;Ask for the name before the budget. Ask for the kind of project before the email. People who've answered three easy questions have invested something, and they finish more often than people who hit a wall at question one. This is the same reason a chat feels lighter than a page of fields: one small ask at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep what they've already typed
&lt;/h2&gt;

&lt;p&gt;This is the one most people skip, and it's where the real money is. If someone answers five of eight questions and closes the tab, those five answers are gone in most form tools unless you turn on partial saving. Gravity Forms calls it partial entries, HubSpot users ask for it in the community forum every month, and the newer builders have it as a toggle.&lt;/p&gt;

&lt;p&gt;Turn it on. A half-finished response with a name and an email is still a lead. It's also how you find out which question is the cliff, because you can see exactly where each person stopped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then email them a way back
&lt;/h2&gt;

&lt;p&gt;Here's what almost nobody does. The person who left at question six was interested enough to answer five. A reminder that lands them on question six, with the first five already filled in, is the cheapest lead you'll ever recover.&lt;/p&gt;

&lt;p&gt;This isn't a hunch. Sauermann and Roach's 2013 study in Research Policy on survey reminders found that varying the timing and wording of follow-ups raised response rates, and that the people who come back after a reminder are not the same people who would have answered anyway.&lt;/p&gt;

&lt;p&gt;How to do it with no special tool: collect the email as question one or two, turn on partial saving, and once a day export the partials and send a short plain-text email with the form link. It takes ten minutes a day. It works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fix the answers, not just the count
&lt;/h2&gt;

&lt;p&gt;Completion is only half the problem. The other half is what's in the completed forms. Ask "what went wrong with the last tool you tried" on a page of fields and you'll get "it was bad", "n/a", and "pricing". Those are rows you throw away.&lt;/p&gt;

&lt;p&gt;Xiao and colleagues published a study in ACM Transactions on Computer-Human Interaction in 2020 comparing a standard web survey with a chat-based one that asked a follow-up when an open answer was thin. The chat version got longer, more specific answers to the open questions. People will say more when something asks them to.&lt;/p&gt;

&lt;p&gt;If your tool can't follow up, the manual version is to write the follow-up into the question itself: "What went wrong, and what were you trying to do when it happened?" Two-part questions get two-part answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built when the manual version got old
&lt;/h2&gt;

&lt;p&gt;Doing the export-and-email loop by hand every morning is fine for a month. Then it's a chore, and the follow-up questions you wrote into the form get skipped by the people in a hurry anyway.&lt;/p&gt;

&lt;p&gt;So this is the part I work on. chatform runs the form as a conversation. When a free-text answer is too thin to use, it asks one more question in plain words and records the real answer. Choices and scales are matched exactly and never sent to a model. When someone leaves, it saves every answer they gave and emails them up to three times, four hours, then a day, then three days later, with a link that opens on the question they stopped at. If they've finished in the meantime, the reminder is dropped.&lt;/p&gt;

&lt;p&gt;Full disclosure: I'm on the team that builds it. The free plan has unlimited responses; the follow-up emails and partial responses are on the paid plan. If the manual loop above is already working for you, you don't need it. If you'd rather not run the loop by hand, the &lt;a href="https://chatform.in/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=form-abandonment" rel="noopener noreferrer"&gt;chatform home page&lt;/a&gt; has a live form you can try before signing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The order, one more time
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Find the question where people leave.&lt;/li&gt;
&lt;li&gt;Make the phone number optional.&lt;/li&gt;
&lt;li&gt;Say how long it takes and show progress.&lt;/li&gt;
&lt;li&gt;Put the easy questions first.&lt;/li&gt;
&lt;li&gt;Save partial answers.&lt;/li&gt;
&lt;li&gt;Email a way back to the exact question.&lt;/li&gt;
&lt;li&gt;Ask a follow-up when the answer is thin.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do the first four this afternoon. They're free in every tool. Then look at your completion rate next week and decide whether the last three are worth automating.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI disclosure: I used an AI assistant to help outline and structure this post. The facts and prices were checked by hand on 20 October 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/p/4f2367ddffda" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>marketing</category>
      <category>ux</category>
      <category>startup</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Typeform Gives You 10 Free Responses a Month. I Tested 6 Tools That Don't.</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Tue, 20 Oct 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/typeform-gives-you-10-free-responses-a-month-i-tested-6-tools-that-dont-3l8d</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/typeform-gives-you-10-free-responses-a-month-i-tested-6-tools-that-dont-3l8d</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkg57xojfhgww481hjs6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fqkg57xojfhgww481hjs6.jpg" alt="chatform, a conversational form, on a laptop and a phone" width="800" height="420"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typeform's free plan stops at 10 responses a month. Not 10 per form. Ten across your whole account. I found this out the way most people do: a form I had shared in a newsletter went quiet on day three, and the dashboard told me the rest.&lt;/p&gt;

&lt;p&gt;I work on chatform, a form builder, so I have an obvious stake here. I'll label my own tool clearly and I'll tell you where the others beat it. I used an AI assistant to help structure this piece and checked every price against the vendor's own pricing page on 20 October 2026.&lt;/p&gt;

&lt;p&gt;No WordPress plugins, no enterprise survey suites. Just six tools you can sign up for today, the real free limit on each, and what breaks when you outgrow it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the 10-response cap matters more than it sounds
&lt;/h2&gt;

&lt;p&gt;Ten responses is a demo, not a plan. A contact form on a small site gets more than that in a week. The first paid plan, Basic, is $25 a month billed yearly or $39 billed monthly, and it caps you at 100 responses. Plus is $79 for 1,000. Business is $129 for 10,000.&lt;/p&gt;

&lt;p&gt;So the question is never "is Typeform good". It is. The question is whether you want to pay per answer for a form that mostly asks for a name and an email.&lt;/p&gt;

&lt;p&gt;Here's what I found when I rebuilt the same six-question lead form in each of these.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Tally: the best free form builder, full stop
&lt;/h2&gt;

&lt;p&gt;Tally gives you unlimited forms and unlimited responses on the free plan. The editor works like a Notion page: you type a question, press slash, pick a block. It is the fastest builder on this list by a wide margin.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; the form is a page of fields or one-per-screen, and that's it. Custom domains, team seats and removing the Tally badge are on Pro at $29 a month. There's no AI, which is a feature or a gap depending on what you need.&lt;/p&gt;

&lt;p&gt;Pick Tally if you want a clean form, unlimited answers, and nothing clever.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Youform: the closest copy of Typeform's look
&lt;/h2&gt;

&lt;p&gt;If what you like about Typeform is the one-question-at-a-time feel with big type and a progress bar, Youform is the nearest match. Unlimited forms and responses free, and Pro starts at $20 a month billed yearly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; it's a format, not a conversation. It shows the next card whatever you typed in the last one. Integrations are thinner than Typeform's.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Fillout: the one to pick if your data lives in Airtable or Notion
&lt;/h2&gt;

&lt;p&gt;Fillout's free plan gives you 1,000 responses a month, and paid starts at $15 a month billed yearly. Its strength is writing straight into Airtable, Notion, Google Sheets and about fifty other tools without a Zapier step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; 1,000 a month is generous, but it is still a cap. The builder has a lot of surface area, which is good once you learn it and slow on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Formbricks: the open-source option you can self-host
&lt;/h2&gt;

&lt;p&gt;Formbricks is AGPLv3 and free to run on your own server with Docker. The hosted cloud has a free plan of 250 responses a month. It was built for in-app product surveys first, so it's strong on targeting who sees a survey and when.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; if you don't want to run a server, 250 a month is the smallest free number on this list. The link-form experience is plainer than the in-app one.&lt;/p&gt;

&lt;p&gt;Pick it if "we must own the data" is a requirement and you have someone who can keep a Docker container alive.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Google Forms: free, unlimited, and ugly on purpose
&lt;/h2&gt;

&lt;p&gt;I'm including it because it is the honest answer for a lot of people. Any Google account, unlimited responses, straight into Sheets. Nobody has ever been fired for using it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; the design is the design. Logic is basic. People fill it in like a tax form, which is to say fast and with as few words as they can get away with.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. chatform (I work on this one)
&lt;/h2&gt;

&lt;p&gt;Full disclosure: this is the tool I help build, so weigh what follows accordingly.&lt;/p&gt;

&lt;p&gt;The free plan has unlimited responses, with a fair-use ceiling of 10,000 a month, across up to 100 forms. Logic, scoring, signatures, file uploads, Google Sheets and webhooks are all on Free. Pro is $16 a month billed yearly.&lt;/p&gt;

&lt;p&gt;The reason it exists is not the price. It's that every tool above records whatever lands in the field. If someone types "it was bad" into "what went wrong", that's the row you get. chatform runs the form as a chat and asks one more question when the answer is too thin to use, then records the real answer. Choice and scale answers are matched exactly and never sent to a model; only free text is read.&lt;/p&gt;

&lt;p&gt;It also emails people who leave halfway, with a link back to the question they stopped on. That part is on Pro.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where it falls short:&lt;/strong&gt; no video questions. One native integration (a spreadsheet feed) plus webhooks and an API, where Typeform has hundreds of connectors. The template gallery is a few hundred, not thousands. There's no Typeform importer, though you can paste the URL of your existing form and it drafts the questions from it.&lt;/p&gt;

&lt;p&gt;You can try it without an account on the &lt;a href="https://chatform.in/typeform-alternative?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=typeform-alternatives" rel="noopener noreferrer"&gt;chatform vs Typeform page&lt;/a&gt;, which also lists its gaps in a table.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest decision tree
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You want the cleanest free form and nothing else:&lt;/strong&gt; Tally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You want it to look like Typeform:&lt;/strong&gt; Youform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your data lives in Airtable or Notion:&lt;/strong&gt; Fillout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You must self-host:&lt;/strong&gt; Formbricks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't care how it looks:&lt;/strong&gt; Google Forms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your problem is thin answers and people who leave halfway:&lt;/strong&gt; chatform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need video questions and a huge integration catalogue, and the budget is there:&lt;/strong&gt; stay on Typeform.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Before you switch, export
&lt;/h2&gt;

&lt;p&gt;Whatever you move to, your old responses stay in the old tool. Export them first. And remember every link you've ever shared points at the old form, so replace the embed on your site and update the link in your bio before you cancel.&lt;/p&gt;

&lt;p&gt;If you've hit the 10-response wall and tried something I missed, tell me in the responses. I'll add it with the same "where it falls short" line as everything else.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;AI disclosure: I used an AI assistant to help outline and structure this post. The facts and prices were checked by hand on 20 October 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://medium.com/p/3ae09a8de4f4" rel="noopener noreferrer"&gt;Medium&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>saas</category>
      <category>startup</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Updating to macOS 27 Golden Gate? Check Your "System Data" First</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Tue, 20 Oct 2026 06:02:43 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/updating-to-macos-27-golden-gate-check-your-system-data-first-3adp</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/updating-to-macos-27-golden-gate-check-your-system-data-first-3adp</guid>
      <description>&lt;p&gt;The Mac subreddits are full of the same post this week. Someone updates to macOS 27 Golden Gate, opens Storage settings, and finds "System Data" at 150 GB. Deleting documents doesn't move the bar. Sometimes it grows back after they've deleted 100 GB.&lt;/p&gt;

&lt;p&gt;I haven't updated yet. I'm still on macOS 26 Tahoe, and my System Data was &lt;strong&gt;already 147 GB of a 245 GB disk&lt;/strong&gt;. So before installing Golden Gate, I split that grey bar into its parts.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv438k583r3kjgj7lbxt.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcv438k583r3kjgj7lbxt.jpeg" alt="System Data on my MacBook, split into named parts: 147 GB before updating (screenshot from Tidy Mac, which I make)" width="800" height="597"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why deleting documents doesn't help
&lt;/h2&gt;

&lt;p&gt;Storage settings only names a few things: Apps, Documents, Photos, Mail, iCloud. Everything else lands in System Data: app data, caches, backups, snapshots, swap. So the "Documents" number is small, and the grey bar is where the space actually went.&lt;/p&gt;

&lt;h2&gt;
  
  
  What was inside mine
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App Support Data, 37.55 GB.&lt;/strong&gt; Folders apps keep in &lt;code&gt;~/Library/Application Support&lt;/code&gt;. On my Mac it's mostly AI tools; the Claude desktop app alone is 13 GB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shared Library &amp;amp; Homebrew, 31.92 GB.&lt;/strong&gt; Developer tools in &lt;code&gt;/opt/homebrew&lt;/code&gt; and shared app data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Virtual memory (swap), 20.44 GB.&lt;/strong&gt; Memory macOS parked on disk. A restart clears most of it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Other Library data, 13.54 GB&lt;/strong&gt;, and &lt;strong&gt;App Containers, 4.9 GB.&lt;/strong&gt; More per-app data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not measurable, 11.21 GB.&lt;/strong&gt; Parts macOS won't let any app measure directly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System caches and temp files, 10.62 GB&lt;/strong&gt;, plus &lt;strong&gt;Caches, 4.77 GB.&lt;/strong&gt; Rebuilt on their own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Developer and AI caches, 8.43 GB.&lt;/strong&gt; Package and model caches.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;System assets and AI models, 3.83 GB.&lt;/strong&gt; Apple Intelligence and other downloaded system parts.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why it jumps after a macOS update
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Time Machine local snapshots.&lt;/strong&gt; macOS keeps snapshots on your own disk. Files you delete stay inside them, which is why space doesn't come back after a big cleanup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Re-indexing.&lt;/strong&gt; After an update, Spotlight and Photos re-scan everything. That fills caches and swap for a day or two.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Apple Intelligence.&lt;/strong&gt; Its models download after the update and count as system storage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The installer itself.&lt;/strong&gt; A major update needs roughly 20–40 GB free. Near full, it fails or the Mac crawls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to do before updating (10 minutes)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Restart. That alone clears most swap.&lt;/li&gt;
&lt;li&gt;Check snapshots in Terminal. If the first command lists any, the second removes them. Backups on your external drive aren't touched.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;tmutil listlocalsnapshots /
&lt;span class="nb"&gt;sudo &lt;/span&gt;tmutil thinlocalsnapshots / 999999999999 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Open Finder › your iPhone › Manage Backups and delete backups of old devices.&lt;/li&gt;
&lt;li&gt;See your biggest app folders:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;If you use Homebrew: &lt;code&gt;brew cleanup --prune=all&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Start the update with at least 30–40 GB free.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What not to delete
&lt;/h2&gt;

&lt;p&gt;Anything in &lt;code&gt;/System&lt;/code&gt;, your &lt;code&gt;~/Library/Preferences&lt;/code&gt;, and the folders of apps you still use. Manage Photos, Mail and Messages from inside those apps. And never "clean" the whole Library folder: move things to the Trash one at a time, so a mistake costs nothing.&lt;/p&gt;

&lt;p&gt;Full disclosure: I build &lt;a href="https://tidymac.space/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=macos27" rel="noopener noreferrer"&gt;Tidy Mac&lt;/a&gt;, the app in the screenshot. It splits System Data into named parts like these and moves what you pick to the Trash with Undo. You don't need it for the steps above. If you just want to know whether one folder is safe to delete, there's a &lt;a href="https://tidymac.space/tools/is-it-safe-to-delete-mac/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=macos27" rel="noopener noreferrer"&gt;free checker&lt;/a&gt;, and a longer guide to &lt;a href="https://tidymac.space/guides/clear-system-data-macos-27/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=macos27" rel="noopener noreferrer"&gt;System Data on macOS 27&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drafted with AI assistance. Every number is from my own Mac.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>mac</category>
      <category>productivity</category>
      <category>apple</category>
    </item>
    <item>
      <title>Homebrew Was Using 25.6 GB of My Mac. Anaconda's Cache Alone Was 5 GB.</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Tue, 20 Oct 2026 04:55:16 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/homebrew-was-using-256-gb-of-my-mac-anacondas-cache-alone-was-5-gb-2hjh</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/homebrew-was-using-256-gb-of-my-mac-anacondas-cache-alone-was-5-gb-2hjh</guid>
      <description>&lt;p&gt;Last week I found AI coding tools hiding 32 GB on my MacBook. Claude, Codex, Cursor and Gemini were all sitting inside the grey "System Data" bar that macOS won't let you open.&lt;/p&gt;

&lt;p&gt;So I kept going. The next biggest thing in there wasn't an app at all. It was &lt;strong&gt;Homebrew: 25.6 GB in &lt;code&gt;/opt/homebrew&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I don't think of Homebrew as big. You type &lt;code&gt;brew install&lt;/code&gt;, something appears, you move on. But every install leaves things behind, and a few of them are enormous.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6tbx27a2d83twvx8cuo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fd6tbx27a2d83twvx8cuo.png" alt="The biggest items in /opt/homebrew on my Mac, measured with APFS clones counted once (image by author)" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Anaconda: 9.17 GB, and 5.21 GB of it is a cache
&lt;/h2&gt;

&lt;p&gt;I installed Anaconda once for a data course. It's still here: 9.17 GB, plus an 882 MB copy of its installer that Homebrew keeps.&lt;/p&gt;

&lt;p&gt;More than half of that, 5.21 GB, is &lt;code&gt;anaconda3/pkgs&lt;/code&gt;: a copy of every package conda has ever downloaded, kept so the next install is faster. Your environments don't need it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;conda clean &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That removes the cached packages and tarballs and leaves your environments alone. If you don't use conda anymore at all:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew uninstall &lt;span class="nt"&gt;--cask&lt;/span&gt; anaconda
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the whole 9.17 GB plus the installer copy.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flutter: 4.1 GB
&lt;/h2&gt;

&lt;p&gt;The Flutter SDK lives in &lt;code&gt;/opt/homebrew/share/flutter&lt;/code&gt;. If you build Flutter apps, keep it. If you tried it once, &lt;code&gt;brew uninstall --cask flutter&lt;/code&gt; gives back 4 GB.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Java versions: about 1 GB
&lt;/h2&gt;

&lt;p&gt;I had &lt;code&gt;openjdk&lt;/code&gt;, &lt;code&gt;openjdk@17&lt;/code&gt; and &lt;code&gt;openjdk@11&lt;/code&gt; installed side by side. Before removing an old one, check what still depends on it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew uses &lt;span class="nt"&gt;--installed&lt;/span&gt; openjdk@11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that prints nothing, it's safe to &lt;code&gt;brew uninstall openjdk@11&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things you installed once and forgot
&lt;/h2&gt;

&lt;p&gt;Hadoop (1.59 GB), MongoDB, Pandoc, Go. Each one was a reason at the time. This lists only the packages &lt;em&gt;you&lt;/em&gt; installed, not their dependencies, which makes forgotten ones easy to spot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew leaves
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The two commands everyone should run
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew cleanup &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;all   &lt;span class="c"&gt;# old versions and every cached download&lt;/span&gt;
brew autoremove            &lt;span class="c"&gt;# dependencies nothing needs anymore&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Neither touches anything you still use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why macOS calls this "System Data"
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;/opt/homebrew&lt;/code&gt; isn't in your home folder, so macOS's Storage settings can't put it under Apps or Documents. It goes into System Data, alongside app caches and AI models. On my Mac, "Shared Library &amp;amp; Homebrew" alone was 32 GB of the 147 GB System Data bar.&lt;/p&gt;

&lt;p&gt;The rule I follow now: caches are always safe, SDKs you don't use are the biggest wins, and anything with your data in it is a decision you make on purpose.&lt;/p&gt;

&lt;p&gt;I build &lt;a href="https://tidymac.space/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=homebrew-disk" rel="noopener noreferrer"&gt;Tidy Mac&lt;/a&gt;, which is how I measured all of this. It breaks System Data into named parts like this one, and anything you clear goes to the Trash first. The commands above work without it.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Drafted with AI assistance. Every number was measured on my own Mac.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>macos</category>
      <category>homebrew</category>
      <category>python</category>
      <category>productivity</category>
    </item>
    <item>
      <title>AI Coding Tools Were Quietly Using 32 GB of My Mac. Claude Alone Took 13.</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Mon, 19 Oct 2026 20:23:09 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/ai-coding-tools-were-quietly-using-32-gb-of-my-mac-claude-alone-took-13-33j5</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/ai-coding-tools-were-quietly-using-32-gb-of-my-mac-claude-alone-took-13-33j5</guid>
      <description>&lt;p&gt;My MacBook told me its disk was almost full. 5.5 GB free out of 245 GB. My Pictures folder is 93 MB. My Documents folder is under 40 MB.&lt;/p&gt;

&lt;p&gt;So where did 240 GB go?&lt;/p&gt;

&lt;p&gt;macOS had an answer, sort of: &lt;strong&gt;"System Data", 147 GB&lt;/strong&gt;. More than half the disk, in one grey bar, with no way to click into it.&lt;/p&gt;

&lt;p&gt;I measured what was inside it, folder by folder. The surprise wasn't old caches or forgotten installers. It was the AI tools I use every day. &lt;strong&gt;Claude, Codex, Cursor, Gemini and a couple of local models were holding about 32 GB between them&lt;/strong&gt;, and almost none of it shows up anywhere you'd think to look.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzjiihyx42hifunvwts38.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzjiihyx42hifunvwts38.jpeg" alt="Later the same day, after a first cleanup: System Data is still 135.66 GB, 60% of a 245 GB disk" width="799" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it all hides in "System Data"
&lt;/h2&gt;

&lt;p&gt;macOS's Storage settings only name a few things: Apps, Documents, Photos, Mail, iCloud. Everything an app saves for itself lands in &lt;code&gt;~/Library/Application Support&lt;/code&gt; or in hidden folders in your home folder like &lt;code&gt;~/.codex&lt;/code&gt; and &lt;code&gt;~/.cache&lt;/code&gt;. macOS counts all of that as System Data.&lt;/p&gt;

&lt;p&gt;AI tools are the heaviest users of exactly those places. They download runtimes, sandboxes and models, and they keep every session you've ever had.&lt;/p&gt;

&lt;p&gt;Here's what I found on my Mac, measured with APFS clones counted once (so the numbers match what you'd actually get back):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Claude desktop app: 13.08 GB&lt;/strong&gt;, of which 10.22 GB is one folder, &lt;code&gt;vm_bundles&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Gemini / Antigravity (&lt;code&gt;~/.gemini&lt;/code&gt;): 5.31 GB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Codex (&lt;code&gt;~/.codex&lt;/code&gt;): 3.6 GB&lt;/strong&gt;, plus 1.99 GB of runtimes in &lt;code&gt;~/.cache/codex-runtimes&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor: 3.56 GB&lt;/strong&gt;, mostly 2.83 GB in &lt;code&gt;globalStorage&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Code (&lt;code&gt;~/.claude&lt;/code&gt;): 2.8 GB&lt;/strong&gt;, 2.61 GB of it old session transcripts&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hugging Face model cache: 952.8 MB&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Whisper speech models: 628.9 MB&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's roughly 32 GB. On a 256 GB MacBook, that's one eighth of the disk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj02sr1pl0wiq7l4a9yde.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fj02sr1pl0wiq7l4a9yde.jpeg" alt="My home folder as a treemap. The AI tools are the hidden dot-folders: .gemini, .codex, .claude and .cache. Personal folders renamed." width="799" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What each one is, and what's safe to delete
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Claude desktop: &lt;code&gt;~/Library/Application Support/Claude&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
The 10 GB &lt;code&gt;vm_bundles&lt;/code&gt; folder is the virtual machine image Claude uses to run tasks in a sandbox. People on Reddit and in Claude's GitHub issues report the same 10–13 GB. You can delete it with Claude quit, but if you use those features it downloads again. &lt;code&gt;Cache&lt;/code&gt; and &lt;code&gt;Code Cache&lt;/code&gt; (about 500 MB here) are safe to clear any time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code: &lt;code&gt;~/.claude/projects&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Every conversation is saved as a JSON file so you can resume it, and a single long session can pass 100 MB. Delete old project folders you'll never resume; you only lose the ability to pick those sessions back up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codex: &lt;code&gt;~/.codex&lt;/code&gt; and &lt;code&gt;~/.cache/codex-runtimes&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Runtimes and packages Codex downloads to run code. The cache folder re-downloads when needed, so it's safe to clear if you're short on space today.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Gemini and Antigravity: &lt;code&gt;~/.gemini&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Google's Antigravity IDE keeps its data and backups here. If you tried Antigravity once and moved on, this is easy space back. If you use it daily, leave it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor: &lt;code&gt;~/Library/Application Support/Cursor&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Careful with this one. &lt;code&gt;User/globalStorage&lt;/code&gt; holds Cursor's settings and chat history database, so don't delete it blindly. Old folders in &lt;code&gt;User/workspaceStorage&lt;/code&gt;, for projects you've deleted, are safe to remove.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hugging Face and Whisper: &lt;code&gt;~/.cache/huggingface&lt;/code&gt; and &lt;code&gt;~/.cache/whisper&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
Downloaded models. Delete the ones you don't use (&lt;code&gt;huggingface-cli delete-cache&lt;/code&gt; lets you pick); anything you delete downloads again the next time a tool asks for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check your own Mac in 30 seconds
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/Library/Application&lt;span class="se"&gt;\ &lt;/span&gt;Support/&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; ~/.[!.]&lt;span class="k"&gt;*&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-h&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first line shows the ten biggest app data folders. The second shows the ten biggest hidden folders in your home. If you use AI coding tools, I'd bet at least three of them are in those lists.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule I follow now
&lt;/h2&gt;

&lt;p&gt;Caches and downloaded models are safe: they come back on their own. Anything that holds history (chat databases, session transcripts) is yours: decide it on purpose. And nothing gets deleted permanently on the first pass. It goes to the Trash first, so a mistake costs you nothing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwzoer0ieuj999xvqvdm9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwzoer0ieuj999xvqvdm9.jpeg" alt="Tidy Mac's Smart Clean separates what regrows on its own from what's worth a look." width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Full disclosure: I build &lt;a href="https://tidymac.space/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai-tools-disk" rel="noopener noreferrer"&gt;Tidy Mac&lt;/a&gt;, the app in these screenshots, because I got tired of doing this with &lt;code&gt;du&lt;/code&gt; every few weeks. It names what's inside System Data, including AI tools and developer caches, with sizes that match Disk Utility, and everything goes to the Trash with Undo. It's a one-time purchase, no subscription.&lt;/p&gt;

&lt;p&gt;But you don't need it to fix this. The two commands above and ten minutes will find most of it. If you want to go deeper on any one folder, I wrote up &lt;a href="https://tidymac.space/learn/mac-application-support-folder/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai-tools-disk" rel="noopener noreferrer"&gt;what's in Application Support&lt;/a&gt;, &lt;a href="https://tidymac.space/folders/huggingface-cache/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai-tools-disk" rel="noopener noreferrer"&gt;the Hugging Face cache&lt;/a&gt; and &lt;a href="https://tidymac.space/learn/what-is-system-data-on-mac/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=ai-tools-disk" rel="noopener noreferrer"&gt;what System Data really is&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If your Mac says it's full and you use Claude, Codex or Cursor, start with those folders. Mine had 32 GB waiting.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>ai</category>
      <category>productivity</category>
      <category>devtools</category>
    </item>
    <item>
      <title>du Said Chrome Was Using 67 GB on My Mac. APFS Said 2. Here's a Script to Tell the Difference</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Sun, 18 Oct 2026 04:36:25 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/du-said-chrome-was-using-67-gb-on-my-mac-apfs-said-2-heres-a-script-to-tell-the-difference-597e</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/du-said-chrome-was-using-67-gb-on-my-mac-apfs-said-2-heres-a-script-to-tell-the-difference-597e</guid>
      <description>&lt;p&gt;My MacBook Air was down to 18 GB free, so I went hunting. The biggest folder on the whole disk, according to &lt;code&gt;du&lt;/code&gt;, was this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/private/var/folders/…/X/com.google.Chrome.code_sign_clone   67G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside: &lt;strong&gt;48 full copies of Google Chrome&lt;/strong&gt;. None of them the version I actually have installed.&lt;/p&gt;

&lt;p&gt;Then I measured it the way APFS actually stores it, and the 67 GB turned into about 2. Here's the folder, the trap, and a 30-line Swift script that measures any folder the same way.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;(Written with help from an AI assistant. Every number and command was run on my own Mac.)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Check yours (read-only)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;getconf DARWIN_USER_TEMP_DIR&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/../X/com.google.Chrome.code_sign_clone"&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;getconf DARWIN_USER_TEMP_DIR&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/../X/"&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mine printed &lt;code&gt;48&lt;/code&gt; and &lt;code&gt;67G&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;From what I can tell, Chrome makes one of these each time it launches after an update. The running browser keeps a validly signed copy of itself while the new version is swapped in underneath. They're meant to be temporary. On my Mac they weren't: 3 copies of Chrome 154 and 45 of 155.0.8059.40, while &lt;code&gt;/Applications&lt;/code&gt; has 155.0.8059.80.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: &lt;code&gt;du&lt;/code&gt; doesn't understand clones
&lt;/h2&gt;

&lt;p&gt;APFS can &lt;em&gt;clone&lt;/em&gt; a file. The clone looks like a full copy to every tool, but it shares blocks with the original until one side changes. Chrome makes its copies as clones.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;du&lt;/code&gt;, Finder's Get Info and most disk-map apps add every clone up at full size. 48 × ~1.4 GB = 67 GB that mostly doesn't exist.&lt;/p&gt;

&lt;p&gt;APFS will tell you the truth, you just have to ask through &lt;code&gt;getattrlist&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ATTR_CMNEXT_CLONEID&lt;/code&gt;: which clone family a file belongs to&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTR_CMNEXT_EXT_FLAGS&lt;/code&gt;: bit 0 means "may share blocks"&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ATTR_CMNEXT_PRIVATESIZE&lt;/code&gt;: bytes that belong only to this file&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So: for each clone, count its private bytes, and count the shared part once per family.&lt;/p&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kd"&gt;import&lt;/span&gt; &lt;span class="kt"&gt;Foundation&lt;/span&gt;

&lt;span class="c1"&gt;// Usage: swift realsize.swift &amp;lt;folder&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// Counts APFS clones once: each clone adds only its private bytes,&lt;/span&gt;
&lt;span class="c1"&gt;// and the shared blocks are counted once per clone family.&lt;/span&gt;
&lt;span class="kd"&gt;func&lt;/span&gt; &lt;span class="nf"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;bits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="nv"&gt;n&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UInt64&lt;/span&gt;&lt;span class="p"&gt;]?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;al&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;attrlist&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bitmapcount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;u_short&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ATTR_BIT_MAP_COUNT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;forkattr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;bits&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kt"&gt;UInt8&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="nv"&gt;repeating&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withUnsafeMutableBytes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="nf"&gt;getattrlist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;al&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;baseAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x21&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nf"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadUnaligned&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;fromByteOffset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;as&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UInt64&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;CommandLine&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;naive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;real&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;Int64&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;families&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;UInt64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;FileManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;atPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;rel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;nextObject&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as?&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;rel&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="nv"&gt;st&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="nf"&gt;lstat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_mode&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="kt"&gt;S_IFMT&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kt"&gt;S_IFREG&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;full&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;st_blocks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;naive&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt;
    &lt;span class="c1"&gt;// ATTR_CMNEXT_CLONEID | ATTR_CMNEXT_EXT_FLAGS; flag 1 = may share blocks&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x100&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mh"&gt;0x200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;priv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;attrs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mh"&gt;0x8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)?[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;real&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;families&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inserted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;real&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="kt"&gt;Int64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="c1"&gt;// shared part, once&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;real&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;full&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;g&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;1_073_741_824.0&lt;/span&gt;  &lt;span class="c1"&gt;// GiB, same unit as du -h&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"du-style: %.1fG   on disk: %.1fG"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;naive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Double&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;real&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;0x21&lt;/code&gt; is &lt;code&gt;FSOPT_ATTR_CMN_EXTENDED | FSOPT_NOFOLLOW&lt;/code&gt;. You need Xcode's command line tools for &lt;code&gt;swift&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;On the Chrome folder:&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="nv"&gt;$ &lt;/span&gt;swift realsize.swift &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;getconf DARWIN_USER_TEMP_DIR&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/../X/com.google.Chrome.code_sign_clone"&lt;/span&gt;
du-style: 67.3G   on disk: 2.1G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;67 GB on paper, about 2 GB on disk.&lt;/strong&gt; Still waste (those clones keep old Chrome versions' blocks alive), but a 30× smaller problem.&lt;/p&gt;

&lt;p&gt;It's a simplification: a file's "shared" part can be shared with files outside the folder, so treat the result as "roughly what deleting this folder gives back". (My app's stricter version credits those blocks to whichever file owns them, and lands on 1.8 GB for the same folder.) The honest final check is always &lt;code&gt;df -h /&lt;/code&gt; before and after (and empty the Trash).&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you delete them?
&lt;/h2&gt;

&lt;p&gt;Probably fine, with two rules: quit Chrome completely first (Cmd-Q), and expect them back after the next update. Open the folder with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;open &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;getconf DARWIN_USER_TEMP_DIR&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/../X"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;…and drag &lt;code&gt;com.google.Chrome.code_sign_clone&lt;/code&gt; to the Trash. Expect a GB or two back, not 67.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I care
&lt;/h2&gt;

&lt;p&gt;Clones are everywhere now: Finder's Duplicate, pnpm's store, Chrome's copies. Any disk tool that sums file sizes will show you folders bigger than your drive, and you'll delete things expecting space you never get.&lt;/p&gt;

&lt;p&gt;I ran into this while building &lt;a href="https://tidymac.space/?utm_source=devto&amp;amp;utm_medium=article&amp;amp;utm_campaign=chrome-clones" rel="noopener noreferrer"&gt;Tidy Mac&lt;/a&gt;, a Mac cleaner. Full disclosure: it's mine. It counts clones once like the script above, labels what's safe to clear, and moves everything to the Trash first.&lt;/p&gt;

&lt;p&gt;How many copies did your Mac have? Post your &lt;code&gt;ls | wc -l&lt;/code&gt; in the comments. I'm curious if anyone beats 48.&lt;/p&gt;

</description>
      <category>macos</category>
      <category>swift</category>
      <category>apple</category>
      <category>productivity</category>
    </item>
    <item>
      <title>'It Was Bad.' 'Bad How?' — What Happens When You Let a Form Ask a Follow-Up Question</title>
      <dc:creator>Md Ayan bag</dc:creator>
      <pubDate>Thu, 01 Oct 2026 17:40:44 +0000</pubDate>
      <link>https://dev.to/md_ayanbag_03a42545b17ac/it-was-bad-bad-how-what-happens-when-you-let-a-form-ask-a-follow-up-question-4e9l</link>
      <guid>https://dev.to/md_ayanbag_03a42545b17ac/it-was-bad-bad-how-what-happens-when-you-let-a-form-ask-a-follow-up-question-4e9l</guid>
      <description>&lt;p&gt;Every form you've ever built has shipped with the same silent bug: it accepts whatever someone types and moves on.&lt;/p&gt;

&lt;p&gt;Someone writes "it was bad" in a feedback field, and your form says, in effect, &lt;em&gt;great, thanks&lt;/em&gt; — and stores three useless words forever. Nobody follows up. Nobody asks "bad how — the price, or something it couldn't do?" The person who typed that answer probably &lt;em&gt;has&lt;/em&gt; a clear reason. Your form just never asked for it.&lt;/p&gt;

&lt;p&gt;That's not a UX nitpick. It's a data quality problem that gets worse the more forms you ship, and it's the specific thing we set out to fix with &lt;a href="https://chatform.in/" rel="noopener noreferrer"&gt;Chatform&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap isn't completion rate, it's answer quality
&lt;/h2&gt;

&lt;p&gt;Most form-builder benchmarking obsesses over completion rate: did the person finish all the fields? That's measurable, so it's what gets optimized. But a 100%-complete form full of one-word non-answers is not a win — it's a worse failure mode than abandonment, because it &lt;em&gt;looks&lt;/em&gt; like success in your dashboard.&lt;/p&gt;

&lt;p&gt;There's actual research behind this, not just a hunch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Xiao et al. (ACM Transactions on Computer-Human Interaction, 27(3), 2020) found that conversational, one-question-at-a-time interfaces produce meaningfully higher-quality open-ended answers than static multi-field forms — people elaborate more when they're asked, not just prompted.&lt;/li&gt;
&lt;li&gt;Sauermann &amp;amp; Roach (Research Policy, 42(1), 2013) showed that follow-up contact (not just the initial ask) is what recovers a huge share of otherwise-lost survey responses — the first send is never the whole story.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Put those together and you get the actual shape of the problem: forms lose good data twice — once when an answer is too thin to be useful, and again when the person leaves before finishing at all. Static forms have no mechanism for either.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "asking a follow-up" actually requires
&lt;/h2&gt;

&lt;p&gt;This is the part that's interesting as a builder, not just as a marketer. Making a form ask a good follow-up question isn't "add a chatbot wrapper around your fields." A few concrete problems you have to solve:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Deciding what counts as "thin."&lt;/strong&gt; A one-word answer to "what's your name?" is correct. A one-word answer to "what went wrong?" is not. You can't apply a single length heuristic — the bar for "sufficient" depends on the question itself, so the system needs context on what each question is actually trying to learn, not just a character count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Knowing when to stop.&lt;/strong&gt; A follow-up engine that always asks "can you say more?" is worse than no follow-up at all — it's just friction with extra steps. The harder (and more important) problem is recognizing a &lt;em&gt;good&lt;/em&gt; answer immediately and moving on without asking twice.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Staying grounded instead of hallucinating next steps.&lt;/strong&gt; If someone asks the form a question mid-flow — "wait, who sees my answers?" — the honest answer has to come from something the form owner actually wrote, not a plausible-sounding guess. That's why Chatform's follow-up and Q&amp;amp;A layer is grounded in a knowledge base you provide, not freeform generation: the form can say "Only the Northwind team. Nothing is shared outside it." because that's literally in the docs it was given, not because it sounds reasonable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Following up after they leave, not just while they're there.&lt;/strong&gt; The Sauermann &amp;amp; Roach finding above is why abandonment recovery can't just be a "continue where you left off" link — it has to be timed (a few hours, then a day, then a few days later tends to outperform a single nag) and it has to resume at the exact question they stopped on, not the start.&lt;/p&gt;

&lt;p&gt;None of these are exotic AI problems, but they're also not "just add GPT to a textarea" — they're closer to interview design translated into a state machine, which is why most form builders that bolt on "AI" do a single rewrite pass on the question text and call it done.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like end to end
&lt;/h2&gt;

&lt;p&gt;Concretely, in Chatform a form is built from: a &lt;strong&gt;persona&lt;/strong&gt; (who's asking), a &lt;strong&gt;goal&lt;/strong&gt; (what the conversation needs to get to), and an optional &lt;strong&gt;knowledge base&lt;/strong&gt; (what it's allowed to answer from). From there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thin answers get a specific, context-aware follow-up — not a generic "could you elaborate?"&lt;/li&gt;
&lt;li&gt;Questions mid-form get answered from your docs, then the conversation picks back up exactly where it left off&lt;/li&gt;
&lt;li&gt;People who abandon get an email that returns them to their unfinished question, not the top of the form&lt;/li&gt;
&lt;li&gt;Everything still exports to the structured fields you'd expect — short text, email, number, address, scored fields — the conversation is the interface, not the data model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And because most teams don't start from zero, you can point it at an existing Typeform, Google Forms, Tally, Jotform or Youform link and it rebuilds the same questions and logic as a conversation, so switching isn't a rebuild-from-scratch decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this doesn't matter
&lt;/h2&gt;

&lt;p&gt;Worth being honest about the edges: if your form is three fields and none of them are open-ended ("email," "company size," "yes/no"), a follow-up layer buys you nothing — there's no thin answer to catch. This is specifically a tool for forms where the &lt;em&gt;value is in the free-text answer&lt;/em&gt;: qualification, applications, research, onboarding, feedback. If every field on your form is a dropdown, stick with whatever you're using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Free tier is actually free — unlimited responses, 200 AI conversations a month, 100 forms, no card required: &lt;a href="https://chatform.in/" rel="noopener noreferrer"&gt;chatform.in&lt;/a&gt;. There's a live demo form on the homepage if you want to see the follow-up behavior before building anything.&lt;/p&gt;

&lt;p&gt;Curious what this community has run into on the data-quality side specifically — not completion rate, but the "technically answered, but useless" problem. Anyone handled this a different way (progressive profiling, mandatory minimum character counts, human follow-up calls)? Genuinely want to hear what's worked and what hasn't.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>saas</category>
      <category>ux</category>
    </item>
  </channel>
</rss>
