<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Shy Dev]]></title><description><![CDATA[Just an engineer's space to share experiences and interests]]></description><link>https://blog.theshydev.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 17:54:29 GMT</lastBuildDate><atom:link href="https://blog.theshydev.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[5 (More) Bash Snippets That Saved My Dev Life]]></title><description><![CDATA[Part 2 of my series on Bash helpers I used during AI app development and system debugging.

Photo by Lukas on Unsplash
Read this first

The provided scripts do not reflect the exact version I used before.

I’ll add the complete versions on my reposit...]]></description><link>https://blog.theshydev.com/5-more-bash-snippets-that-saved-my-dev-life</link><guid isPermaLink="true">https://blog.theshydev.com/5-more-bash-snippets-that-saved-my-dev-life</guid><category><![CDATA[Bash]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Devops]]></category><category><![CDATA[shell script]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Thu, 22 May 2025 21:15:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/4Mw7nkQDByk/upload/f1ffb14455be2905af3f24dfdbb7ffd6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<p><em>Part 2 of my series on Bash helpers I used during AI app development and system debugging.</em></p>
<p><img src="https://cdn-images-1.medium.com/max/720/0*yM04VY8pEP5YWNTT" alt /></p>
<p>Photo by <a target="_blank" href="https://unsplash.com/@lukash?utm_source=medium&amp;utm_medium=referral">Lukas</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<h3 id="heading-read-this-first">Read this first</h3>
<ul>
<li><p>The provided scripts do not reflect the exact version I used before.</p>
</li>
<li><p>I’ll add the complete versions on my repository soon: <a target="_blank" href="https://github.com/the-shy-dev/bash-snippets-gallery">https://github.com/the-shy-dev/bash-snippets-gallery</a></p>
</li>
<li><p>I would love to see how you would implement these ideas. Please share them with me.</p>
</li>
<li><p>Part 1 here: <a target="_blank" href="https://blog.theshydev.com/5-bash-snippets-that-saved-my-dev-life-on-linux-devices">https://blog.theshydev.com/5-bash-snippets-that-saved-my-dev-life-on-linux-devices</a></p>
</li>
</ul>
<h3 id="heading-1-smart-orphan-amp-resource-killer">1. Smart Orphan &amp; Resource Killer</h3>
<p>While testing AI inference applications that spawn subprocesses, I often encountered zombie or orphaned processes due to bugs in my early code. I manually killed them at first — but it got old quickly. Eventually, I extended my helper script to also flag non-zombie processes that were hogging CPU or memory. Now, it prompts me before termination, unless I want to run it in dry-run mode or kill everything automatically.</p>
<h3 id="heading-what-it-does">What it does</h3>
<ul>
<li><p>Detects and kills zombie processes automatically.</p>
</li>
<li><p>Lists high-resource processes and prompts: do you want to kill them?</p>
</li>
<li><p>Displays PID, CPU%, MEM%, and command line for clarity.</p>
</li>
<li><p>Offers a <code>--dry-run</code> to simulate what would be killed.</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
CPU_THRESHOLD=50
MEM_THRESHOLD=50
DRY_RUN=<span class="hljs-literal">false</span>
AUTO_KILL=<span class="hljs-literal">false</span>
<span class="hljs-function"><span class="hljs-title">print_usage</span></span>() {
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> [--cpu &lt;percent&gt;] [--mem &lt;percent&gt;] [--dry-run] [--auto]"</span>
    <span class="hljs-built_in">exit</span> 1
}
<span class="hljs-comment"># Parse Arguments</span>
<span class="hljs-keyword">while</span> [[ <span class="hljs-variable">$#</span> -gt 0 ]]; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> <span class="hljs-keyword">in</span>
        --cpu)
            CPU_THRESHOLD=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --mem)
            MEM_THRESHOLD=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --dry-run)
            DRY_RUN=<span class="hljs-literal">true</span>
            <span class="hljs-built_in">shift</span>
            ;;
        --auto)
            AUTO_KILL=<span class="hljs-literal">true</span>
            <span class="hljs-built_in">shift</span>
            ;;
        *)
            print_usage
            ;;
    <span class="hljs-keyword">esac</span>
<span class="hljs-keyword">done</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Scanning for zombie processes..."</span>
ZOMBIES=$(ps -eo pid,<span class="hljs-built_in">stat</span>,cmd | awk <span class="hljs-string">'$2 ~ /Z/ { print $1 }'</span>)
<span class="hljs-keyword">if</span> [[ -n <span class="hljs-string">"<span class="hljs-variable">$ZOMBIES</span>"</span> ]]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] Found zombie processes: <span class="hljs-variable">$ZOMBIES</span>"</span>
    <span class="hljs-keyword">for</span> PID <span class="hljs-keyword">in</span> <span class="hljs-variable">$ZOMBIES</span>; <span class="hljs-keyword">do</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[-] Killing zombie PID <span class="hljs-variable">$PID</span>"</span>
        <span class="hljs-built_in">kill</span> -9 <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span>
    <span class="hljs-keyword">done</span>
<span class="hljs-keyword">else</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[+] No zombie processes found."</span>
<span class="hljs-keyword">fi</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">""</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Checking for high CPU/RAM usage..."</span>
ps -eo pid,comm,%cpu,%mem,cmd --sort=-%cpu | tail -n +2 | <span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> -r PID COMM CPU MEM CMD; <span class="hljs-keyword">do</span>
    CPU_INT=<span class="hljs-variable">${CPU%.*}</span>
    MEM_INT=<span class="hljs-variable">${MEM%.*}</span>
    <span class="hljs-keyword">if</span> (( CPU_INT &gt;= CPU_THRESHOLD || MEM_INT &gt;= MEM_THRESHOLD )); <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-subst">$(date)</span>] High usage detected: PID=<span class="hljs-variable">$PID</span>, CPU=<span class="hljs-variable">$CPU</span>%, MEM=<span class="hljs-variable">$MEM</span>%, CMD=<span class="hljs-variable">$CMD</span>"</span>
        <span class="hljs-keyword">if</span> <span class="hljs-variable">$DRY_RUN</span>; <span class="hljs-keyword">then</span>
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"    &gt;&gt; DRY RUN: Would prompt or kill process <span class="hljs-variable">$PID</span>"</span>
        <span class="hljs-keyword">elif</span> <span class="hljs-variable">$AUTO_KILL</span>; <span class="hljs-keyword">then</span>
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"    &gt;&gt; AUTO: Killing process <span class="hljs-variable">$PID</span>"</span>
            <span class="hljs-built_in">kill</span> -9 <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span>
        <span class="hljs-keyword">else</span>
            <span class="hljs-built_in">echo</span> -n <span class="hljs-string">"    &gt;&gt; Kill process <span class="hljs-variable">$PID</span>? (y/N/q): "</span>
            <span class="hljs-built_in">read</span> -r REPLY
            <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$REPLY</span>"</span> == <span class="hljs-string">"y"</span> || <span class="hljs-string">"<span class="hljs-variable">$REPLY</span>"</span> == <span class="hljs-string">"Y"</span> ]]; <span class="hljs-keyword">then</span>
                <span class="hljs-built_in">kill</span> -9 <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span>
                <span class="hljs-built_in">echo</span> <span class="hljs-string">"    &gt;&gt; Process <span class="hljs-variable">$PID</span> killed."</span>
            <span class="hljs-keyword">elif</span> [[ <span class="hljs-string">"<span class="hljs-variable">$REPLY</span>"</span> == <span class="hljs-string">"q"</span> || <span class="hljs-string">"<span class="hljs-variable">$REPLY</span>"</span> == <span class="hljs-string">"Q"</span> ]]; <span class="hljs-keyword">then</span>
                <span class="hljs-built_in">echo</span> <span class="hljs-string">"    &gt;&gt; Exiting helper."</span>
                <span class="hljs-built_in">break</span>
            <span class="hljs-keyword">else</span>
                <span class="hljs-built_in">echo</span> <span class="hljs-string">"    &gt;&gt; Skipped."</span>
            <span class="hljs-keyword">fi</span>
        <span class="hljs-keyword">fi</span>
    <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-2-config-diff-tracker-for-ai-experiments">2. Config Diff Tracker for AI Experiments</h3>
<p>When experimenting with AI model behavior, we constantly tweaked a <code>config.json</code> that controls the application logic. After many changes, we forgot which combination led to success or failure. So I made this tracker that logs full config snapshots and diffs to keep track of iterations.</p>
<h3 id="heading-what-it-does-1">What it does</h3>
<ul>
<li><p>Watches a target config file (default: <code>config.json</code>).</p>
</li>
<li><p>When modified, saves a timestamped copy.</p>
</li>
<li><p>Also creates a <code>diff</code> against the last saved version.</p>
</li>
<li><p>User can control what to save using <code>--mode full</code>, <code>diff</code>, or <code>both</code>.</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
TARGET_FILE=<span class="hljs-string">"config.json"</span>
SNAPSHOT_DIR=<span class="hljs-string">"/opt/file_diff_snapshots"</span>
MODE=<span class="hljs-string">"both"</span>
<span class="hljs-keyword">while</span> [[ <span class="hljs-variable">$#</span> -gt 0 ]]; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> <span class="hljs-keyword">in</span>
        --file)
            TARGET_FILE=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --snapshot-dir)
            SNAPSHOT_DIR=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --mode)
            MODE=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        *)
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> [--file &lt;path&gt;] [--snapshot-dir &lt;path&gt;] [--mode full|diff|both]"</span>
            <span class="hljs-built_in">exit</span> 1
            ;;
    <span class="hljs-keyword">esac</span>
<span class="hljs-keyword">done</span>
mkdir -p <span class="hljs-string">"<span class="hljs-variable">$SNAPSHOT_DIR</span>"</span>
BASENAME=$(basename <span class="hljs-string">"<span class="hljs-variable">$TARGET_FILE</span>"</span>)
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LATEST_SNAPSHOT=$(ls -t <span class="hljs-string">"<span class="hljs-variable">$SNAPSHOT_DIR</span>"</span>/*_<span class="hljs-string">"<span class="hljs-variable">$BASENAME</span>"</span> 2&gt;/dev/null | head -n 1)
NEW_SNAPSHOT=<span class="hljs-string">"<span class="hljs-variable">$SNAPSHOT_DIR</span>/<span class="hljs-variable">${TIMESTAMP}</span>_<span class="hljs-variable">$BASENAME</span>"</span>
cp <span class="hljs-string">"<span class="hljs-variable">$TARGET_FILE</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$NEW_SNAPSHOT</span>"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Saved snapshot: <span class="hljs-variable">$NEW_SNAPSHOT</span>"</span>
<span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$MODE</span>"</span> == <span class="hljs-string">"diff"</span> || <span class="hljs-string">"<span class="hljs-variable">$MODE</span>"</span> == <span class="hljs-string">"both"</span> ]] &amp;&amp; [[ -n <span class="hljs-string">"<span class="hljs-variable">$LATEST_SNAPSHOT</span>"</span> &amp;&amp; <span class="hljs-string">"<span class="hljs-variable">$LATEST_SNAPSHOT</span>"</span> != <span class="hljs-string">"<span class="hljs-variable">$NEW_SNAPSHOT</span>"</span> ]]; <span class="hljs-keyword">then</span>
    DIFF_FILE=<span class="hljs-string">"<span class="hljs-variable">$SNAPSHOT_DIR</span>/<span class="hljs-variable">${TIMESTAMP}</span>_diff.patch"</span>
    diff -u <span class="hljs-string">"<span class="hljs-variable">$LATEST_SNAPSHOT</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$NEW_SNAPSHOT</span>"</span> &gt; <span class="hljs-string">"<span class="hljs-variable">$DIFF_FILE</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Saved diff: <span class="hljs-variable">$DIFF_FILE</span>"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<h3 id="heading-3-safe-ffmpeg-recorder">3. Safe FFmpeg Recorder</h3>
<p>Edge devices like Jetsons and Pi have limited disk space. While recording video feeds via USB or IP cameras, I needed a safeguard to stop <code>ffmpeg</code> before it filled the drive and crashed the system. This script handles it for me.</p>
<h3 id="heading-what-it-does-2">What it does</h3>
<ul>
<li><p>Starts an <code>ffmpeg</code> recording session.</p>
</li>
<li><p>Monitors available disk space.</p>
</li>
<li><p>If either <code>--min-mb</code> or <code>--min-pct</code> is breached, it terminates recording gracefully.</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
DEVICE=<span class="hljs-string">"/dev/video0"</span>
OUTPUT=<span class="hljs-string">"recording_<span class="hljs-subst">$(date +%Y%m%d_%H%M%S)</span>.mp4"</span>
MIN_MB=500
MIN_PCT=10
<span class="hljs-keyword">while</span> [[ <span class="hljs-variable">$#</span> -gt 0 ]]; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> <span class="hljs-keyword">in</span>
        --input)
            DEVICE=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --output)
            OUTPUT=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --min-mb)
            MIN_MB=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --min-pct)
            MIN_PCT=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        *)
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> [--input /dev/videoX] [--output file.mp4] [--min-mb X] [--min-pct Y]"</span>
            <span class="hljs-built_in">exit</span> 1
            ;;
    <span class="hljs-keyword">esac</span>
<span class="hljs-keyword">done</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Starting ffmpeg recording..."</span>
ffmpeg -f v4l2 -i <span class="hljs-string">"<span class="hljs-variable">$DEVICE</span>"</span> -vcodec libx264 -preset ultrafast <span class="hljs-string">"<span class="hljs-variable">$OUTPUT</span>"</span> &amp;
PID=$!
<span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">do</span>
    AVAIL_MB=$(df --output=avail -m . | tail -1)
    TOTAL_MB=$(df --output=size -m . | tail -1)
    AVAIL_PCT=$((AVAIL_MB * <span class="hljs-number">100</span> / TOTAL_MB))
    <span class="hljs-keyword">if</span> (( AVAIL_MB &lt; MIN_MB || AVAIL_PCT &lt; MIN_PCT )); <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] Low space: <span class="hljs-variable">$AVAIL_MB</span> MB (<span class="hljs-variable">${AVAIL_PCT}</span>%). Stopping ffmpeg..."</span>
        <span class="hljs-built_in">kill</span> -INT <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span>
        <span class="hljs-built_in">break</span>
    <span class="hljs-keyword">fi</span>
    sleep 5
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-4-checksum-integrity-watchdog">4. Checksum Integrity Watchdog</h3>
<p>I needed to ensure that post-deployment configs and binaries hadn’t been tampered with or accidentally modified. Especially when troubleshooting unexpected behavior. So this script became my watchdog.</p>
<h3 id="heading-what-it-does-3">What it does</h3>
<ul>
<li><p>Supports a file or entire directory.</p>
</li>
<li><p><code>--init</code>: saves current checksums.</p>
</li>
<li><p><code>--check</code>: verifies once and reports diffs.</p>
</li>
<li><p><code>--start</code>: begins looped monitoring in background.</p>
</li>
<li><p><code>--stop</code>: halts the background watchdog.</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
TARGET_PATH=<span class="hljs-string">""</span>
MODE=<span class="hljs-string">""</span>
CHECKSUM_FILE=<span class="hljs-string">"/tmp/checksums.txt"</span>
PID_FILE=<span class="hljs-string">"/tmp/checksum_watchdog.pid"</span>

<span class="hljs-function"><span class="hljs-title">print_usage</span></span>() {
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> &lt;path&gt; --init|--check|--start|--stop"</span>
    <span class="hljs-built_in">exit</span> 1
}
<span class="hljs-comment"># Generate SHA256 checksums to stdout</span>
<span class="hljs-function"><span class="hljs-title">generate_checksums</span></span>() {
    <span class="hljs-keyword">if</span> [ -d <span class="hljs-string">"<span class="hljs-variable">$TARGET_PATH</span>"</span> ]; <span class="hljs-keyword">then</span>
        find <span class="hljs-string">"<span class="hljs-variable">$TARGET_PATH</span>"</span> -<span class="hljs-built_in">type</span> f -<span class="hljs-built_in">exec</span> sha256sum {} +
    <span class="hljs-keyword">elif</span> [ -f <span class="hljs-string">"<span class="hljs-variable">$TARGET_PATH</span>"</span> ]; <span class="hljs-keyword">then</span>
        sha256sum <span class="hljs-string">"<span class="hljs-variable">$TARGET_PATH</span>"</span>
    <span class="hljs-keyword">else</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] Invalid path: <span class="hljs-variable">$TARGET_PATH</span>"</span>
        <span class="hljs-built_in">exit</span> 1
    <span class="hljs-keyword">fi</span>
}
<span class="hljs-comment"># Compare saved and current checksums</span>
<span class="hljs-function"><span class="hljs-title">compare_checksums</span></span>() {
    TMP_FILE=$(mktemp)
    generate_checksums &gt; <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Checking for changes..."</span>
    diff -u <span class="hljs-string">"<span class="hljs-variable">$CHECKSUM_FILE</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span>
    rm <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span>
}
<span class="hljs-comment"># Monitor in a loop</span>
<span class="hljs-function"><span class="hljs-title">watch_loop</span></span>() {
    <span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">do</span>
        TMP_FILE=$(mktemp)
        generate_checksums &gt; <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span>
        <span class="hljs-keyword">if</span> ! diff -q <span class="hljs-string">"<span class="hljs-variable">$CHECKSUM_FILE</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span> &gt; /dev/null; <span class="hljs-keyword">then</span>
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] Detected file changes at <span class="hljs-subst">$(date)</span>"</span>
            diff -u <span class="hljs-string">"<span class="hljs-variable">$CHECKSUM_FILE</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span>
        <span class="hljs-keyword">fi</span>
        rm <span class="hljs-string">"<span class="hljs-variable">$TMP_FILE</span>"</span>
        sleep 10
    <span class="hljs-keyword">done</span>
}
<span class="hljs-comment"># Ensure at least two arguments (path + mode)</span>
<span class="hljs-keyword">if</span> [[ <span class="hljs-variable">$#</span> -lt 2 ]]; <span class="hljs-keyword">then</span>
    print_usage
<span class="hljs-keyword">fi</span>
<span class="hljs-comment"># Parse arguments</span>
<span class="hljs-keyword">while</span> [[ <span class="hljs-variable">$#</span> -gt 0 ]]; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> <span class="hljs-keyword">in</span>
        --init)
            MODE=<span class="hljs-string">"init"</span>; <span class="hljs-built_in">shift</span> ;;
        --check)
            MODE=<span class="hljs-string">"check"</span>; <span class="hljs-built_in">shift</span> ;;
        --start)
            MODE=<span class="hljs-string">"start"</span>; <span class="hljs-built_in">shift</span> ;;
        --stop)
            MODE=<span class="hljs-string">"stop"</span>; <span class="hljs-built_in">shift</span> ;;
        *)
            TARGET_PATH=<span class="hljs-string">"<span class="hljs-variable">$1</span>"</span>; <span class="hljs-built_in">shift</span> ;;
    <span class="hljs-keyword">esac</span>
<span class="hljs-keyword">done</span>
<span class="hljs-comment"># Execute mode</span>
<span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">$MODE</span>"</span> <span class="hljs-keyword">in</span>
    init)
        generate_checksums &gt; <span class="hljs-string">"<span class="hljs-variable">$CHECKSUM_FILE</span>"</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[+] Initial checksums saved to <span class="hljs-variable">$CHECKSUM_FILE</span>"</span>
        ;;
    check)
        compare_checksums
        ;;
    start)
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Starting checksum monitor in background..."</span>
        watch_loop &amp;
        <span class="hljs-built_in">echo</span> $! &gt; <span class="hljs-string">"<span class="hljs-variable">$PID_FILE</span>"</span>
        ;;
    stop)
        <span class="hljs-keyword">if</span> [ -f <span class="hljs-string">"<span class="hljs-variable">$PID_FILE</span>"</span> ]; <span class="hljs-keyword">then</span>
            <span class="hljs-built_in">kill</span> <span class="hljs-string">"<span class="hljs-subst">$(cat <span class="hljs-string">"<span class="hljs-variable">$PID_FILE</span>"</span>)</span>"</span> &amp;&amp; rm <span class="hljs-string">"<span class="hljs-variable">$PID_FILE</span>"</span>
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"[+] Watchdog stopped."</span>
        <span class="hljs-keyword">else</span>
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] No watchdog running."</span>
        <span class="hljs-keyword">fi</span>
        ;;
    *)
        print_usage
        ;;
<span class="hljs-keyword">esac</span>
</code></pre>
<h3 id="heading-5-offline-log-notifier-with-websocket-reporting">5. Offline Log Notifier with WebSocket Reporting</h3>
<p>During offline debugging of embedded Linux devices, I wanted to know if critical log patterns like <code>segfault</code>, <code>oom</code>, or <code>panic</code> occurred—even without Internet. This script watches logs and ships matching alerts to a WebSocket endpoint once network resumes.</p>
<h3 id="heading-what-it-does-4">What it does</h3>
<ul>
<li><p>Watches log file (<code>dmesg</code> or any path).</p>
</li>
<li><p>Filters lines using a configurable regex.</p>
</li>
<li><p>Sends alerts to a WebSocket server.</p>
</li>
<li><p>Optionally runs in console-only mode (<code>--local-mode</code>).</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
LOGFILE=<span class="hljs-string">"/var/log/dmesg"</span>
KEYWORDS=<span class="hljs-string">"segfault|oom|panic"</span>
ENDPOINT=<span class="hljs-string">""</span>
LOCAL_ONLY=<span class="hljs-literal">false</span>
<span class="hljs-keyword">while</span> [[ <span class="hljs-variable">$#</span> -gt 0 ]]; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">case</span> <span class="hljs-string">"<span class="hljs-variable">$1</span>"</span> <span class="hljs-keyword">in</span>
        --logfile)
            LOGFILE=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --keywords)
            KEYWORDS=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --endpoint)
            ENDPOINT=<span class="hljs-string">"<span class="hljs-variable">$2</span>"</span>
            <span class="hljs-built_in">shift</span> 2
            ;;
        --local-mode)
            LOCAL_ONLY=<span class="hljs-literal">true</span>
            <span class="hljs-built_in">shift</span>
            ;;
        *)
            <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> [--logfile file] [--keywords regex] [--endpoint ws://...] [--local-mode]"</span>
            <span class="hljs-built_in">exit</span> 1
            ;;
    <span class="hljs-keyword">esac</span>
<span class="hljs-keyword">done</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Watching <span class="hljs-variable">$LOGFILE</span> for keywords: <span class="hljs-variable">$KEYWORDS</span>"</span>
tail -Fn0 <span class="hljs-string">"<span class="hljs-variable">$LOGFILE</span>"</span> | <span class="hljs-keyword">while</span> <span class="hljs-built_in">read</span> -r LINE; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">if</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$LINE</span>"</span> | grep -Ei <span class="hljs-string">"<span class="hljs-variable">$KEYWORDS</span>"</span> &gt; /dev/null; <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] Matched log: <span class="hljs-variable">$LINE</span>"</span>
        <span class="hljs-keyword">if</span> ! <span class="hljs-variable">$LOCAL_ONLY</span> &amp;&amp; [[ -n <span class="hljs-string">"<span class="hljs-variable">$ENDPOINT</span>"</span> ]]; <span class="hljs-keyword">then</span>
            curl -s -X POST -H <span class="hljs-string">"Content-Type: application/json"</span> -d <span class="hljs-string">"{\"message\": \"<span class="hljs-variable">$LINE</span>\"}"</span> <span class="hljs-string">"<span class="hljs-variable">$ENDPOINT</span>"</span>
        <span class="hljs-keyword">fi</span>
    <span class="hljs-keyword">fi</span>
<span class="hljs-keyword">done</span>
</code></pre>
<hr />
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>These aren’t polished, one-size-fits-all tools. Most of them were born out of need and evolved over time. There might be more improvements or logical improvements I missed.</p>
<p><strong>Do you have a Bash trick or script that saved your dev life?</strong> Drop it in the comments — I’d love to hear it from you!</p>
<p>#linux#bash#programming#shellscripting#tipsandtricks</p>
]]></content:encoded></item><item><title><![CDATA[My First macOS Disappointment - External Display Limitations on the M4 MacBook Pro]]></title><description><![CDATA[Part 1 of a series on discovering the friction points in Apple’s polished ecosystem.


Photo by AB on Unsplash
When I unboxed my new MacBook Pro 14-inch with the M4 chip, I wasn’t exactly thrilled. Don’t get me wrong — I respect the hardware, the bat...]]></description><link>https://blog.theshydev.com/my-first-macos-disappointment-external-display-limitations-on-the-m4-macbook-pro</link><guid isPermaLink="true">https://blog.theshydev.com/my-first-macos-disappointment-external-display-limitations-on-the-m4-macbook-pro</guid><category><![CDATA[displaylink]]></category><category><![CDATA[macbook]]></category><category><![CDATA[macOS]]></category><category><![CDATA[limitations]]></category><category><![CDATA[m4]]></category><category><![CDATA[MacBook Pro]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Wed, 21 May 2025 23:00:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/mP7aPSUm7aE/upload/06132b76b0bc33a0b2aad0cd29471ee3.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><em>Part 1 of a series on discovering the friction points in Apple’s polished ecosystem.</em></p>
</blockquote>
<p><img src="https://cdn-images-1.medium.com/max/1080/0*2k8cQphQ_TNA5KeI" alt /></p>
<p>Photo by <a target="_blank" href="https://unsplash.com/@applefanboy?utm_source=medium&amp;utm_medium=referral">AB</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<p>When I unboxed my new <strong>MacBook Pro 14-inch with the M4 chip</strong>, I wasn’t exactly thrilled. Don’t get me wrong — I respect the hardware, the battery life, and the performance Apple Silicon offers. But something about macOS always made me feel… boxed in.</p>
<p>Unfortunately, it didn’t take long for that feeling to be validated.</p>
<h3 id="heading-the-setup-that-works-on-windows">The Setup That Works (on Windows)</h3>
<p>My personal setup includes <strong>three external monitors</strong>, all connected via a <strong>USB-C hub</strong> with:</p>
<ul>
<li><p>2x HDMI</p>
</li>
<li><p>1x DisplayPort</p>
</li>
</ul>
<p>This setup works flawlessly on my <strong>Lenovo ThinkPad X1 Carbon</strong>:</p>
<pre><code class="lang-typescript"><span class="hljs-number">3</span> external monitors ➝ USB-C Hub ➝ ThinkPad (Windows)
</code></pre>
<p>Each display functions independently — I can drag apps across screens, debug code while watching logs, and have media playing off to the side. Simple and effective.</p>
<h3 id="heading-the-same-setup-on-macos-disaster">The Same Setup on macOS? Disaster.</h3>
<blockquote>
<p><em>Device Info: MacBook Pro 14 inch, M4, Nov 2024.</em></p>
</blockquote>
<p>When I plugged the same hub into my brand-new <strong>MacBook Pro M4</strong>, I was greeted with… three identical screens.</p>
<p>No extended displays. No flexibility. Just a single <strong>mirrored desktop copy-pasted across all three monitors</strong>.</p>
<p>As a sane person, I tried all possible things: checking system settings, browsing online, changing cables (weird choice but whatever)</p>
<p>And this isn’t a hardware issue. It’s a <strong>macOS limitation</strong> — by design.</p>
<h3 id="heading-why-this-happens-its-not-just-you">Why This Happens: It’s Not Just You</h3>
<h3 id="heading-macos-doesnt-support-mst-multi-stream-transport">macOS Doesn’t Support MST (Multi-Stream Transport)</h3>
<p><strong>MST</strong> allows chaining or splitting multiple displays through one port. It’s a core part of DisplayPort and USB-C video delivery.</p>
<ul>
<li><p><strong>Windows</strong> and <strong>Linux</strong> support MST natively.</p>
</li>
<li><p><strong>macOS</strong> never has.</p>
</li>
</ul>
<p>So even though your <strong>M4 MacBook Pro hardware technically supports it</strong>, the <strong>macOS software stack disables it</strong> entirely.</p>
<p>That’s why:</p>
<ul>
<li><p>Your USB-C hub mirrors displays.</p>
</li>
<li><p>macOS can’t differentiate those signals as separate outputs.</p>
</li>
</ul>
<p>If you booted into Linux or Windows (via external drive), it would likely work. <em>TBD: I’ll find reference and add them here.</em></p>
<h3 id="heading-apples-official-display-limits">Apple’s Official Display Limits</h3>
<p>From Apple’s official documentation:</p>
<blockquote>
<p><em>“MacBook Pro (14-inch, M4) supports up to two external displays.” One via</em> <strong><em>Thunderbolt</em></strong> <em>+ one via</em> <strong><em>HDMI</em></strong>*.*</p>
</blockquote>
<p>🔗 <a target="_blank" href="https://support.apple.com/en-us/101571">Apple Support — How many displays can it support?</a></p>
<p>You’ll never get three extended displays without a workaround.</p>
<h3 id="heading-workarounds-that-actually-work">Workarounds That Actually Work</h3>
<h3 id="heading-1-displaylink-dock-with-caveats">1. DisplayLink Dock (With Caveats)</h3>
<p>DisplayLink docks simulate multiple displays via software and USB. They <strong>bypass macOS’s native display system</strong>.</p>
<p>Pros:</p>
<ul>
<li><p>Adds more displays even on limited Macs</p>
</li>
<li><p>Just one USB-C port needed</p>
</li>
</ul>
<p>Cons:</p>
<ul>
<li><p><strong>Not future-proof</strong> (macOS updates can break it)</p>
</li>
<li><p><strong>Performance issues</strong> (lag, screen tearing, limited refresh rates)</p>
</li>
<li><p>No proper <strong>DRM</strong> support (Netflix, Apple TV might fail)</p>
</li>
<li><p>Extra software layer always running in the background</p>
</li>
</ul>
<h3 id="heading-2-stick-with-dual-displays-officially-supported">2. Stick With Dual Displays (Officially Supported)</h3>
<p>If you want no drama, use one or two displays as specified on Apple Support page. I chose this approach as I don’t want to spend money on an expensive ThunderBolt4 dock and here’s a reference to my setup: <a target="_blank" href="https://www.reddit.com/r/macbookpro/comments/1gn473m/comment/mrf5of6/?utm_source=share&amp;utm_medium=web3x&amp;utm_name=web3xcss&amp;utm_term=1&amp;utm_content=share_button">Reddit — r/macbookpro</a></p>
<p>You’ll get proper resolution, GPU acceleration, and fewer headaches — but you’re capped at 2 displays.</p>
<h3 id="heading-3-use-sidecar-universal-control">3. Use Sidecar / Universal Control</h3>
<p>If you own an iPad, <strong>Sidecar</strong> adds an extra pseudo-display. It’s decent for reference apps, music, or notes — but not a full-fledged monitor replacement.</p>
<h3 id="heading-some-curious-takes-i-found">Some Curious Takes I Found</h3>
<p>While researching all of this, I came across <strong>forum responses from other Mac users</strong>:</p>
<ul>
<li><p>“Why do people even need more than two monitors?”</p>
</li>
<li><p>“Just close your MacBook lid and use dual screens.”</p>
</li>
<li><p>“Windows uses too much CPU/GPU when handling multiple monitors — macOS is smarter.”</p>
</li>
<li><p>“Apple must have a reason for not supporting MST. It’s probably better for battery life.”</p>
</li>
<li><p>“You’re using the wrong hub. Just buy a proper Thunderbolt dock instead of blaming Apple.”</p>
</li>
<li><p>“Too many screens just make people unproductive. This is a feature, not a limitation.”</p>
</li>
<li><p>“Professionals use one screen and focus — that’s the Apple way.”</p>
</li>
</ul>
<p>I get it. Everyone has different workflows, and some people genuinely don’t need more than one or two screens.</p>
<p>But I couldn’t help but feel a little surprised.</p>
<p>Not because people work differently — but because many seemed eager to <strong>rationalize limitations instead of recognizing them</strong>. Instead of asking Apple to do better, it felt like users were asking each other to <strong>settle for less</strong>.</p>
<p><strong>It’s okay to love a product and still point out its flaws.</strong> That’s how platforms evolve. That’s how user needs are heard.</p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>The M4 MacBook Pro is an advanced piece of hardware — battery life, performance, thermals. But I’m starting to feel Apple prioritizes <strong>elegance over flexibility</strong> — even if it hampers power users.</p>
<p>If you’re coming from Windows or Linux with a complex, multi-screen workflow — brace yourself. You’ll either need to <strong>downgrade your expectations</strong> or <strong>invest in fragile workarounds</strong>.</p>
<h3 id="heading-tldr">TL;DR</h3>
<ul>
<li><p>macOS doesn’t support <strong>MST</strong>, so USB-C hubs with multiple outputs won’t extend your displays.</p>
</li>
<li><p>M4 MacBook Pro supports <strong>only 2 external displays</strong> natively.</p>
</li>
<li><p><strong>DisplayLink</strong> docks work, but they’re fragile and add lag.</p>
</li>
<li><p>macOS can’t <strong>control external display volume</strong> natively.</p>
</li>
<li><p>HDMI-CEC features are <strong>incomplete</strong> on macOS.</p>
</li>
<li><p>Some Mac users justify these limitations — but that doesn’t make them less frustrating.</p>
</li>
</ul>
<h3 id="heading-curious-to-hear-from-you">Curious To Hear From You</h3>
<p>If you’ve built a stable triple monitor setup on macOS — how did you do it? Do you feel Apple should prioritize power-user flexibility more?</p>
<p>Drop your experience in the comments</p>
<p>#MacBookPro#macOSLimitations#ExternalMonitor#DisplayLink#MST#M4</p>
]]></content:encoded></item><item><title><![CDATA[Why I Decided to Start Writing Tech Blogs]]></title><description><![CDATA[Image credits: Photo by Nick Morrison on Unsplash

“The best way to learn is to teach.”

I used to think that was just a feel-good quote. Now, I see it as a compass.
If you’re an engineer, enthusiast, or someone trying to break into tech, you’ve prob...]]></description><link>https://blog.theshydev.com/why-i-decided-to-start-writing-tech-blogs</link><guid isPermaLink="true">https://blog.theshydev.com/why-i-decided-to-start-writing-tech-blogs</guid><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[writing]]></category><category><![CDATA[Blogging]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Developer]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Sun, 18 May 2025 05:02:19 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/_UeY8aTI6d0/upload/f6d2c7d5a41325c3a6699205d32e384c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><img src="https://images.unsplash.com/photo-1501504905252-473c47e087f8?q=80&amp;w=3948&amp;auto=format&amp;fit=crop&amp;ixlib=rb-4.0.3&amp;ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt class="image--center mx-auto" /></p>
<p>Image credits: Photo by <a target="_blank" href="https://unsplash.com/@nickmorrison?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Nick Morrison</a> on <a target="_blank" href="https://unsplash.com/photos/macbook-pro-near-white-open-book-FHnnjk1Yj7Y?utm_content=creditCopyText&amp;utm_medium=referral&amp;utm_source=unsplash">Unsplash</a></p>
<blockquote>
<p><em>“The best way to learn is to teach.”</em></p>
</blockquote>
<p>I used to think that was just a feel-good quote. Now, I see it as a compass.</p>
<p>If you’re an engineer, enthusiast, or someone trying to break into tech, you’ve probably felt this: <strong>There’s no shortage of resources-but there’s a shortage of context.</strong></p>
<p>That gap between <em>knowing</em> and <em>doing</em> is where I’ve spent most of my career. And that’s exactly the space I want to write about.</p>
<h3 id="heading-from-debugging-code-to-designing-systems-and-everything-in-between">From Debugging Code to Designing Systems (and Everything in Between)</h3>
<p>I began my journey in tech without knowing the local language, straight out of college, working in Japan. Since then, I’ve done backend engineering, developing for Linux and Android, tinkered with custom AI solutions, custom libraries, and worked with specialized hardware and edge devices.</p>
<p>I’ve led development on various stages, contributed to product roadmaps, and even dived into infrastructure setup-wearing multiple hats in startups and fast-moving teams. But here’s the truth: <strong>Each new role and responsibility taught me how much I still didn’t know.</strong></p>
<p>That humbling realization is what keeps me learning-and now, writing.</p>
<h3 id="heading-why-im-writing">Why I’m Writing</h3>
<ol>
<li><p><strong>To Share Without Sugarcoating</strong><br /> Most tech blogs filter out the rough edges. But the real learning often comes from the messy middle-things that didn’t go right, weird bugs that took days, or design choices that made sense… until they didn’t.</p>
</li>
<li><p><strong>To Clarify My Thinking Through a Third-Person Lens</strong><br /> Writing forces me to step outside myself and view my work, assumptions, and beliefs objectively. It often unlocks new patterns of thought I’d miss otherwise.</p>
</li>
<li><p><strong>To Share My Failures So You Don’t Have To Repeat Them</strong><br /> I’ve made architectural mistakes. I’ve overengineered solutions. I’ve chased shiny tech instead of solving the actual problem. If writing about these saves someone else a few hours-or a few months-it’s worth it.</p>
</li>
<li><p><strong>To Learn From You</strong><br /> This isn’t just about broadcasting ideas. I want to engage. Whether you’re an aspiring dev or a seasoned engineer with a different lens, I’d love to hear your takes.</p>
</li>
</ol>
<h3 id="heading-what-you-can-expect">What You Can Expect</h3>
<p>This won’t be a niche-only blog. It will be as chaotic and random as my brain. In general, you’ll see posts on:</p>
<ul>
<li><p><strong>How I’m integrating AI into traditional systems</strong></p>
</li>
<li><p><strong>Lessons from building scalable backend systems with real-world constraints</strong></p>
</li>
<li><p><strong>How Android development taught me the importance of resource constraints</strong></p>
</li>
<li><p><strong>The curious bugs I find, and the elegant (or ugly) ways I solve them</strong></p>
</li>
<li><p><strong>Reflections on leadership as someone who prefers building over managing</strong></p>
</li>
</ul>
<p>You’ll also see me explore new tech stacks and areas I’m diving into, like Rust, LLM pipelines, or hardware-software interfacing, and documenting what works, what doesn’t, and what’s exciting.</p>
<h3 id="heading-why-this-matters-to-you">Why This Matters (To You)</h3>
<p>If you’re:</p>
<ul>
<li><p>starting out and want to peek into the messy reality behind clean tutorials</p>
</li>
<li><p>transitioning from one domain (say, Android) to another (like AI)</p>
</li>
<li><p>trying to avoid common traps in system design, DevOps, or architecture</p>
</li>
<li><p><em>other interesting things :D</em></p>
</li>
</ul>
<p>I hope this blog feels like a conversation with a learner/peer/mentor based on how far you are. I’ll share not just the <em>how</em>, but also the <em>why</em> and the <em>what I’d do differently if I had to start again</em>.</p>
<p>Because sometimes, the best way to grow fast is to <strong>stand on someone else’s failure</strong>.</p>
<h3 id="heading-lets-learn-together">Let’s Learn Together</h3>
<p>Writing isn’t something I’ve done regularly before. This is a new habit I’m building.<br />I can’t promise a fixed schedule (yet), but I can assure authenticity, curiosity, and relevance.</p>
<p>This is an open space: for learning, sharing, and growing together.<br />Whether you’re here to learn something new, challenge a view, or just lurk and reflect-I’m glad you’re here.</p>
<hr />
<p><strong>What’s one mistake you wish someone had warned you about early in your tech journey?</strong></p>
<p>Drop it in the comments. Maybe we can turn it into a post-or at least a good conversation.</p>
<p>Until next time</p>
]]></content:encoded></item><item><title><![CDATA[5 Bash Snippets That Saved My Dev Life on Linux Devices]]></title><description><![CDATA[From flaky USB cameras to Docker crashes — these quick scripts helped me survive the wild west of edge devices debugging


Photo by Lukas on Unsplash
When you’re in the trenches with Linux-based systems — working with USB/IP cameras, Dockerized apps—...]]></description><link>https://blog.theshydev.com/5-bash-snippets-that-saved-my-dev-life-on-linux-devices</link><guid isPermaLink="true">https://blog.theshydev.com/5-bash-snippets-that-saved-my-dev-life-on-linux-devices</guid><category><![CDATA[shell script]]></category><category><![CDATA[Bash]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Sat, 26 Apr 2025 20:00:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/NLSXFjl_nhc/upload/0628790e18c4e4ad2f12ae0b5a38e642.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>From flaky USB cameras to Docker crashes — these quick scripts helped me survive the wild west of edge devices debugging</p>
</blockquote>
<p><img src="https://cdn-images-1.medium.com/max/1080/0*CDsdYBMqNlBM_gkT" alt /></p>
<p>Photo by <a target="_blank" href="https://unsplash.com/@lukash?utm_source=medium&amp;utm_medium=referral">Lukas</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<p>When you’re in the trenches with Linux-based systems — working with USB/IP cameras, Dockerized apps— you end up writing a lot of ‘just-make-it-work’ Bash scripts.</p>
<h3 id="heading-read-this-first">Read this first</h3>
<ul>
<li><p>The provided scripts do not show the exact version I used before.</p>
</li>
<li><p>I’ll add the complete versions to my repository on a later date: <a target="_blank" href="https://github.com/the-shy-dev/bash-snippets-gallery">https://github.com/the-shy-dev/bash-snippets-gallery</a></p>
</li>
<li><p>To be honest, I would love to see how you would implement these ideas. Please share them with me.</p>
</li>
</ul>
<hr />
<h3 id="heading-1-usb-camera-sanity-test-script-confidence-in-30-seconds">1. USB Camera Sanity Test Script: Confidence in 30 Seconds</h3>
<p>When I plugged in a USB camera, I needed three things immediately:</p>
<ul>
<li><p>Is the device detected?</p>
</li>
<li><p>Can I preview and save frames?</p>
</li>
<li><p>Can I log the camera’s capabilities for future debugging?</p>
</li>
</ul>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
DEVICE=<span class="hljs-string">"/dev/video0"</span>
FRAME_DIR=<span class="hljs-string">"./camera_test_output"</span>
INFO_LOG=<span class="hljs-string">"<span class="hljs-variable">$FRAME_DIR</span>/camera_info.log"</span>
mkdir -p <span class="hljs-string">"<span class="hljs-variable">$FRAME_DIR</span>"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Checking USB camera at <span class="hljs-variable">$DEVICE</span>..."</span>
<span class="hljs-keyword">if</span> v4l2-ctl --list-devices | grep -q <span class="hljs-string">"<span class="hljs-variable">$DEVICE</span>"</span>; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[+] Device detected."</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Logging camera capabilities..."</span>
    v4l2-ctl --device=<span class="hljs-string">"<span class="hljs-variable">$DEVICE</span>"</span> --all &gt; <span class="hljs-string">"<span class="hljs-variable">$INFO_LOG</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Previewing camera feed..."</span>
    ffplay -f v4l2 -framerate 25 -video_size 640x480 -i <span class="hljs-string">"<span class="hljs-variable">$DEVICE</span>"</span> &amp;
    sleep 3
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Capturing sample frames..."</span>
    <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> {1..3}; <span class="hljs-keyword">do</span>
        ffmpeg -f v4l2 -video_size 640x480 -i <span class="hljs-string">"<span class="hljs-variable">$DEVICE</span>"</span> -frames:v 1 <span class="hljs-string">"<span class="hljs-variable">$FRAME_DIR</span>/frame_<span class="hljs-variable">$i</span>.jpg"</span> -loglevel quiet
        sleep 1
    <span class="hljs-keyword">done</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Listing available formats and resolutions..."</span>
    v4l2-ctl --device=<span class="hljs-string">"<span class="hljs-variable">$DEVICE</span>"</span> --list-formats-ext &gt;&gt; <span class="hljs-string">"<span class="hljs-variable">$INFO_LOG</span>"</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[+] Output saved in <span class="hljs-variable">$FRAME_DIR</span>"</span>
<span class="hljs-keyword">else</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[!] Device not found at <span class="hljs-variable">$DEVICE</span>. Is it plugged in?"</span>
<span class="hljs-keyword">fi</span>
</code></pre>
<h3 id="heading-why-its-useful">Why it’s useful:</h3>
<ul>
<li><p>Logs full camera capabilities.</p>
</li>
<li><p>Captures sample frames.</p>
</li>
<li><p>Provides live preview.</p>
</li>
<li><p>Helps debug camera compatibility without launching a full app.</p>
</li>
</ul>
<h3 id="heading-2-process-resource-monitor-with-basic-fault-handling">2. Process Resource Monitor with Basic Fault Handling</h3>
<p>This script gives you a simple CSV of CPU, RAM, and network stats for any process you care about. The data could be further processed via a simple python script to represent visually.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
PROCESS_NAME=<span class="hljs-variable">$1</span>
LOGFILE=<span class="hljs-string">"monitor_<span class="hljs-variable">${PROCESS_NAME}</span>.log"</span>
NET_INTERFACE=<span class="hljs-string">"eth0"</span>
<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$PROCESS_NAME</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> &lt;process_name&gt;"</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Monitoring '<span class="hljs-variable">$PROCESS_NAME</span>' every 5 seconds"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"Timestamp,CPU(%),Memory(%),Net(TX_KB),Net(RX_KB),Status"</span> &gt; <span class="hljs-string">"<span class="hljs-variable">$LOGFILE</span>"</span>
<span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">do</span>
    TIMESTAMP=$(date +%Y-%m-%dT%H:%M:%S)
    PID=$(pgrep -n <span class="hljs-string">"<span class="hljs-variable">$PROCESS_NAME</span>"</span>)
    <span class="hljs-keyword">if</span> [ -n <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span> ]; <span class="hljs-keyword">then</span>
        CPU=$(ps -p <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span> -o %cpu --no-headers | xargs)
        MEM=$(ps -p <span class="hljs-string">"<span class="hljs-variable">$PID</span>"</span> -o %mem --no-headers | xargs)
        TX=$(cat /sys/class/net/<span class="hljs-variable">$NET_INTERFACE</span>/statistics/tx_bytes 2&gt;/dev/null || <span class="hljs-built_in">echo</span> 0)
        RX=$(cat /sys/class/net/<span class="hljs-variable">$NET_INTERFACE</span>/statistics/rx_bytes 2&gt;/dev/null || <span class="hljs-built_in">echo</span> 0)
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$TIMESTAMP</span>,<span class="hljs-variable">$CPU</span>,<span class="hljs-variable">$MEM</span>,<span class="hljs-subst">$((TX/1024)</span>),<span class="hljs-subst">$((RX/1024)</span>),OK"</span> &gt;&gt; <span class="hljs-string">"<span class="hljs-variable">$LOGFILE</span>"</span>
    <span class="hljs-keyword">else</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$TIMESTAMP</span>,0,0,0,0,NOT FOUND"</span> &gt;&gt; <span class="hljs-string">"<span class="hljs-variable">$LOGFILE</span>"</span>
    <span class="hljs-keyword">fi</span>
    sleep 5
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-why-its-useful-1">Why it’s useful:</h3>
<ul>
<li><p>Logs everything to a CSV.</p>
</li>
<li><p>Helps you understand essential performance metrics.</p>
</li>
</ul>
<h3 id="heading-3-intelligent-network-reconnection-script-wi-fi-ethernet">3. Intelligent Network Reconnection Script (Wi-Fi + Ethernet)</h3>
<p>I’ve used this on everything from Jetsons to Raspberry Pi to edge Linux boxes. If Ethernet is used, it resets the interface. If Wi-Fi, it tries reconnecting intelligently.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
INTERFACE=<span class="hljs-variable">$1</span>
WIFI_SSID=<span class="hljs-variable">$2</span>
WIFI_PASS=<span class="hljs-variable">$3</span>
CHECK_INTERVAL=10
<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$INTERFACE</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> &lt;interface&gt; [wifi_ssid] [wifi_password]"</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>
<span class="hljs-function"><span class="hljs-title">is_wireless</span></span>() {
    iw dev | grep -q <span class="hljs-string">"<span class="hljs-variable">$INTERFACE</span>"</span>
}
<span class="hljs-function"><span class="hljs-title">reconnect_wifi</span></span>() {
    <span class="hljs-keyword">if</span> nmcli dev wifi | grep -q <span class="hljs-string">"<span class="hljs-variable">$WIFI_SSID</span>"</span>; <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-subst">$(date)</span>] Attempting to reconnect to known Wi-Fi <span class="hljs-variable">$WIFI_SSID</span>..."</span>
        nmcli dev wifi connect <span class="hljs-string">"<span class="hljs-variable">$WIFI_SSID</span>"</span> password <span class="hljs-string">"<span class="hljs-variable">$WIFI_PASS</span>"</span> iface <span class="hljs-string">"<span class="hljs-variable">$INTERFACE</span>"</span>
    <span class="hljs-keyword">else</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-subst">$(date)</span>] Wi-Fi SSID not found. Retrying later..."</span>
    <span class="hljs-keyword">fi</span>
}
<span class="hljs-function"><span class="hljs-title">restart_eth</span></span>() {
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-subst">$(date)</span>] Restarting Ethernet interface <span class="hljs-variable">$INTERFACE</span>..."</span>
    sudo ip link <span class="hljs-built_in">set</span> <span class="hljs-string">"<span class="hljs-variable">$INTERFACE</span>"</span> down &amp;&amp; sleep 2
    sudo ip link <span class="hljs-built_in">set</span> <span class="hljs-string">"<span class="hljs-variable">$INTERFACE</span>"</span> up
}
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Monitoring network on <span class="hljs-variable">$INTERFACE</span>"</span>
<span class="hljs-keyword">while</span> <span class="hljs-literal">true</span>; <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">if</span> ! ping -I <span class="hljs-string">"<span class="hljs-variable">$INTERFACE</span>"</span> -c 1 8.8.8.8 &gt; /dev/null 2&gt;&amp;1; <span class="hljs-keyword">then</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-subst">$(date)</span>] Network unreachable on <span class="hljs-variable">$INTERFACE</span>"</span>
        <span class="hljs-keyword">if</span> is_wireless; <span class="hljs-keyword">then</span>
            reconnect_wifi
        <span class="hljs-keyword">else</span>
            restart_eth
        <span class="hljs-keyword">fi</span>
    <span class="hljs-keyword">else</span>
        <span class="hljs-built_in">echo</span> <span class="hljs-string">"[<span class="hljs-subst">$(date)</span>] Network OK on <span class="hljs-variable">$INTERFACE</span>"</span>
    <span class="hljs-keyword">fi</span>
    sleep <span class="hljs-string">"<span class="hljs-variable">$CHECK_INTERVAL</span>"</span>
<span class="hljs-keyword">done</span>
</code></pre>
<h3 id="heading-why-its-useful-2">Why it’s useful:</h3>
<ul>
<li><p>Automatically recovers from lost connections.</p>
</li>
<li><p>Handles both Wi-Fi and Ethernet with appropriate logic.</p>
</li>
<li><p>Keeps headless devices online.</p>
</li>
</ul>
<h3 id="heading-4-docker-debug-collector-auto-zip-diagnostic-logs">4. Docker Debug Collector (Auto-Zip Diagnostic Logs)</h3>
<p>This one is a life-saver when your Docker app fails and you want to collect <em>everything</em> before restarting or reporting the bug.</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
CONTAINER_NAME=<span class="hljs-variable">$1</span>
OUTPUT_DIR=<span class="hljs-string">"docker_debug_<span class="hljs-subst">$(date +%Y%m%d_%H%M%S)</span>"</span>
ZIP_FILE=<span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>.zip"</span>
<span class="hljs-keyword">if</span> [ -z <span class="hljs-string">"<span class="hljs-variable">$CONTAINER_NAME</span>"</span> ]; <span class="hljs-keyword">then</span>
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"Usage: <span class="hljs-variable">$0</span> &lt;container_name&gt;"</span>
    <span class="hljs-built_in">exit</span> 1
<span class="hljs-keyword">fi</span>
mkdir -p <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Collecting Docker system info..."</span>
docker images &gt; <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>/images.txt"</span>
docker ps -a &gt; <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>/containers.txt"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Capturing logs for container: <span class="hljs-variable">$CONTAINER_NAME</span>"</span>
docker inspect <span class="hljs-string">"<span class="hljs-variable">$CONTAINER_NAME</span>"</span> &gt; <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>/<span class="hljs-variable">${CONTAINER_NAME}</span>_inspect.json"</span>
docker logs <span class="hljs-string">"<span class="hljs-variable">$CONTAINER_NAME</span>"</span> &gt; <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>/<span class="hljs-variable">${CONTAINER_NAME}</span>_logs.txt"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Copying mounted volumes (if any)..."</span>
docker inspect --format <span class="hljs-string">'{{ range .Mounts }}{{ .Source }} -&gt; {{ .Destination }}\n{{ end }}'</span> <span class="hljs-string">"<span class="hljs-variable">$CONTAINER_NAME</span>"</span> &gt; <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>/<span class="hljs-variable">${CONTAINER_NAME}</span>_mounts.txt"</span>
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[*] Zipping all collected logs..."</span>
zip -r <span class="hljs-string">"<span class="hljs-variable">$ZIP_FILE</span>"</span> <span class="hljs-string">"<span class="hljs-variable">$OUTPUT_DIR</span>"</span> &gt; /dev/null
<span class="hljs-built_in">echo</span> <span class="hljs-string">"[+] Debug data archived to <span class="hljs-variable">$ZIP_FILE</span>"</span>
</code></pre>
<h3 id="heading-why-its-useful-3">Why it’s useful:</h3>
<ul>
<li><p>Grabs container logs, mounts, inspect data, and Docker status in one go.</p>
</li>
<li><p>Great for creating bug reports or investigating crashes after-the-fact.</p>
</li>
</ul>
<h3 id="heading-5-real-time-file-watcher-for-custom-actions-inotify">5. Real-Time File Watcher for Custom Actions (inotify)</h3>
<p>Imagine a file drops in a folder, and you want to process or back it up <em>immediately</em>. This script listens for new files using <code>inotifywait</code>.</p>
<pre><code class="lang-typescript"><span class="hljs-meta">#!/bin/bash</span>
WATCH_DIR=<span class="hljs-string">"/home/user/input_dir"</span>
ACTION_SCRIPT=<span class="hljs-string">"./process_file.sh"</span> # Assume <span class="hljs-built_in">this</span> is another script that handles files
echo <span class="hljs-string">"[*] Watching $WATCH_DIR for new files..."</span>
inotifywait -m -e create --format <span class="hljs-string">"%f"</span> <span class="hljs-string">"$WATCH_DIR"</span> | <span class="hljs-keyword">while</span> read FILE
<span class="hljs-keyword">do</span>
    echo <span class="hljs-string">"[$(date)] New file detected: $FILE"</span>
    <span class="hljs-string">"$ACTION_SCRIPT"</span> <span class="hljs-string">"$WATCH_DIR/$FILE"</span>
done
</code></pre>
<h3 id="heading-why-its-useful-4">Why it’s useful:</h3>
<ul>
<li><p>No need for polling loops.</p>
</li>
<li><p>Automates pipeline triggers as soon as data arrives.</p>
</li>
<li><p>Very handy for camera footage processing or ETL-like setups.</p>
</li>
</ul>
<hr />
<h3 id="heading-bonus-nohup-my-unsung-hero-for-remote-scripting">Bonus: <code>nohup</code> – My Unsung Hero for Remote Scripting</h3>
<p>Okay, this one’s not a script — but it ran all my scripts. When you’re working with edge devices — especially over SSH or in unstable environments — you quickly realize that losing your shell session mid-execution is a nightmare. That’s where <code>nohup</code> came to my rescue.</p>
<p>I used it constantly while running these very scripts on real devices in the field:</p>
<ul>
<li><p>The <strong>network reconnection script</strong> kept headless Jetson and Pi devices online, even when Wi-Fi dropped.</p>
</li>
<li><p>The <strong>process monitor</strong> ran quietly in the background, logging performance of my Docker apps overnight.</p>
</li>
<li><p>The <strong>file watcher</strong> script processed incoming camera footage while I focused on other tasks.</p>
</li>
<li><p>or Any other installer scripts I prepared in the past that can run quite long.</p>
</li>
</ul>
<p>Instead of setting up <code>tmux</code> or <code>systemd</code> immediately, I often used:</p>
<pre><code class="lang-bash">nohup ./network_watchdog.sh &gt; watchdog.log 2&gt;&amp;1 &amp;
</code></pre>
<p>This kept the script alive even after SSH disconnects, power hiccups, or accidental terminal closures — an absolute lifesaver when deploying to remote or industrial environments.</p>
<p><strong>Pros:</strong></p>
<ul>
<li><p>Doesn’t die with your terminal.</p>
</li>
<li><p>Simple and fast to use for long-running scripts.</p>
</li>
<li><p>Logs output to file for post-mortem debugging.</p>
</li>
</ul>
<p><strong>Cons:</strong></p>
<ul>
<li><p>If you forget to redirect output, it writes to <code>nohup.out</code> in your working directory.</p>
</li>
<li><p>It doesn’t survive reboots — consider <code>systemd</code>, <code>supervisord</code>, or cron <code>@reboot</code> if needed.</p>
</li>
</ul>
<h3 id="heading-bonus-2-use-logrotate-to-keep-log-files-clean">Bonus 2: Use <code>logrotate</code> to Keep Log Files Clean</h3>
<p>An amazing helper command to keep things clean. Running long-term scripts with <code>nohup</code> means log files can grow <strong>fast</strong>. To avoid disk space issues, set up log rotation to clean files based on desired conditions.</p>
<p>Here’s a sample logrotate configuration file:</p>
<pre><code class="lang-bash">/home/nvidia/*.<span class="hljs-built_in">log</span> {
    daily
    rotate 7
    compress
    missingok
    notifempty
    create 644 user user
}
</code></pre>
<p>This configuration:</p>
<ul>
<li><p>Rotates logs daily</p>
</li>
<li><p>Keeps logs for the last 7 days</p>
</li>
<li><p>Compresses older logs to save space</p>
</li>
<li><p>Skips empty or missing files</p>
</li>
<li><p>Creates new logs with correct permissions</p>
</li>
</ul>
<p>Without <code>logrotate</code>, <code>nohup</code> logs can silently eat up your disk. With it, you get Reliable background scripts (<code>nohup</code>), Persistent logs without bloat (<code>logrotate</code>). I consider this as a low-effort, high-impact combo for any serious Linux development setup.</p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>These scripts weren’t born in clean labs or tidy tutorials — they came out of panic, frustration, and real-world chaos: broken cameras, flaky Wi-Fi, silent crashes on remote machines, unstable applications.</p>
<p>If you’ve ever SSH’d into a device and whispered things like:</p>
<blockquote>
<p><em>“Please work…”</em></p>
<p><em>“What’s happening!?”</em></p>
<p><em>“How do I know it’s still alive?”</em></p>
</blockquote>
<p>…then you know exactly why scripts like these matter.</p>
<p>They helped me bring some sanity to systems that weren’t built to be predictable — and maybe they’ll help you too.</p>
<p><strong>Do you have a Bash trick or script that saved your dev life?</strong> Drop it in the comments — I’d love to hear it from you!</p>
]]></content:encoded></item><item><title><![CDATA[10 Common Mistakes When Building REST APIs (and How to Avoid Them)]]></title><description><![CDATA[REST APIs seem simple-until you try building a real one. Whether you’re designing internal services or public endpoints, mistakes in structure, naming, or error handling can haunt you for months.

Photo by Francisco De Legarreta C. on Unsplash
Read t...]]></description><link>https://blog.theshydev.com/10-common-mistakes-when-building-rest-apis-and-how-to-avoid-them</link><guid isPermaLink="true">https://blog.theshydev.com/10-common-mistakes-when-building-rest-apis-and-how-to-avoid-them</guid><category><![CDATA[APIs]]></category><category><![CDATA[backend]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Design]]></category><category><![CDATA[REST API]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Sat, 26 Apr 2025 18:00:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/hHg9MC-G8_Y/upload/ea0e71495e4b1ffa68285dd820e2d0c4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>REST APIs seem simple-until you try building a real one. Whether you’re designing internal services or public endpoints, mistakes in structure, naming, or error handling can haunt you for months.</p>
<p><img src="https://cdn-images-1.medium.com/max/1080/0*mBhKA__jSV1SdBLw" alt /></p>
<p>Photo by <a target="_blank" href="https://unsplash.com/@francisco_legarreta?utm_source=medium&amp;utm_medium=referral">Francisco De Legarreta C.</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<h3 id="heading-read-this-first">Read this first</h3>
<ul>
<li>Mistakes are common everywhere. Don’t fret it too much. Never stop learning.</li>
</ul>
<p>Here are 10 common (but fixable) mistakes developers make when building REST APIs-and what to do instead.</p>
<h3 id="heading-1-using-verbs-in-endpoints">1. Using Verbs in Endpoints</h3>
<p>REST is resource-oriented. The <strong>HTTP method</strong> already describes the action. If you are using verbs to define your endpoint, it will be redundant.</p>
<pre><code class="lang-typescript">GET /createUser  
POST /deleteOrder
</code></pre>
<p>You can fix this by using nouns for endpoints and verbs only in HTTP methods.</p>
<pre><code class="lang-typescript">POST /users        <span class="hljs-comment">// create user  </span>
DELETE /orders/:id <span class="hljs-comment">// delete order</span>
</code></pre>
<h3 id="heading-2-ignoring-status-codes">2. Ignoring Status Codes</h3>
<p>This confuses clients that rely on status codes for logic (think: frontend, mobile apps, API consumers).</p>
<pre><code class="lang-typescript">res.status(<span class="hljs-number">200</span>).json({ error: <span class="hljs-string">"User not found"</span> });
</code></pre>
<p>Utilize the status code based on their meaning. Return appropriate status codes-<code>200</code>, <code>201</code>, <code>400</code>, <code>404</code>, <code>500</code>... Don't make everything a <code>200</code>.</p>
<pre><code class="lang-typescript">res.status(<span class="hljs-number">404</span>).json({ error: <span class="hljs-string">"User not found"</span> });
</code></pre>
<h3 id="heading-3-exposing-internal-errors-directly">3. Exposing Internal Errors Directly</h3>
<p>From security point of view, it could leak some sensitive information or give clues to intruders.</p>
<pre><code class="lang-typescript">res.status(<span class="hljs-number">500</span>).json({ message: err.message }); <span class="hljs-comment">// might expose stack traces or DB internals</span>
</code></pre>
<p>Carefully review your implementation. Log errors internally if required.</p>
<pre><code class="lang-typescript">res.status(<span class="hljs-number">500</span>).json({ error: <span class="hljs-string">"Internal server error"</span> });
myLogger.logError(err); <span class="hljs-comment">// log the detailed error internally</span>
</code></pre>
<h3 id="heading-4-not-versioning-your-api">4. Not Versioning Your API</h3>
<p>Excluding version route in your endpoint can lead to potential future issues</p>
<pre><code class="lang-typescript">GET /users
<span class="hljs-comment">// How do you release breaking changes later?</span>
</code></pre>
<p>A simple way to fix it would be include version info in your endpoint. Make this habit from day one.</p>
<pre><code class="lang-typescript">GET /v1/users
</code></pre>
<p>Alternatively, you could use headers (<code>Accept-Version</code>).</p>
<h3 id="heading-5-overloading-200-ok-for-everything">5. Overloading 200 OK for Everything</h3>
<p>This sounds familiar to an earlier mistake. Using 200 everytime might be syntactically OK but it gives a false confidence to clients.</p>
<pre><code class="lang-typescript">res.status(<span class="hljs-number">200</span>).json({ success: <span class="hljs-literal">false</span>, error: <span class="hljs-string">"Bad input"</span> });
</code></pre>
<p>Be semantically correct as well. Don’t lie with 200.</p>
<pre><code class="lang-typescript">res.status(<span class="hljs-number">400</span>).json({ error: <span class="hljs-string">"Bad input"</span> });
</code></pre>
<h3 id="heading-6-inconsistent-naming-and-pluralization">6. Inconsistent Naming and Pluralization</h3>
<pre><code class="lang-typescript">GET /user  
GET /orders  
POST /order
</code></pre>
<p>There’s no strict rule for it but assume plural noun to be the norm to make your life (and your future-self’s life) easier.</p>
<pre><code class="lang-typescript">GET /users  
GET /orders  
POST /orders
</code></pre>
<h3 id="heading-7-cramming-too-much-into-one-endpoint">7. Cramming Too Much Into One Endpoint</h3>
<p>The most common reason for an endpoint to grow insanely huge-it’s convenient.</p>
<pre><code class="lang-typescript">GET /users?includeOrders=<span class="hljs-literal">true</span>&amp;includeComments=<span class="hljs-literal">true</span>&amp;includeProfile=<span class="hljs-literal">true</span>
</code></pre>
<p>There are many ways to fix this. Let’s go with a basic idea of breaking down the endpoint by resources.</p>
<pre><code class="lang-typescript">GET <span class="hljs-string">`/users/:id`</span>
GET <span class="hljs-string">`/users/:id/orders`</span>
GET <span class="hljs-string">`/users/:id/profile`</span>
</code></pre>
<h3 id="heading-8-no-rate-limiting-or-throttling">8. No Rate Limiting or Throttling</h3>
<p>A classic but costly mistake. Anyone can hit your API a few thousand times a second and you won’t notice until the server dies. (The intention is not always with a DDoS intent… but it could be!)</p>
<p>Always rate-limit your APIs.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Simple example with middleware</span>
<span class="hljs-keyword">import</span> rateLimit <span class="hljs-keyword">from</span> <span class="hljs-string">'express-rate-limit'</span>;
app.use(rateLimit({
  windowMs: <span class="hljs-number">1</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>,
  max: <span class="hljs-number">100</span>,
}));
</code></pre>
<h3 id="heading-9-trusting-the-client-a-bit-too-much-poor-input-validation">9. Trusting the Client a bit too much (Poor Input Validation)</h3>
<p>Clients don’t always send what you expect-sometimes due to bugs, sometimes due to… creative users. This leads to garbage data, crashes, or worse-security issues.</p>
<pre><code class="lang-typescript">app.post(<span class="hljs-string">'/users'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> { email, age } = req.body; <span class="hljs-comment">// assumes everything's fine</span>
  <span class="hljs-comment">// ...</span>
});
</code></pre>
<p>Always validate your input. There are various libraries to make it easy for you — Zod, Yup, Joi, etc.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { z } <span class="hljs-keyword">from</span> <span class="hljs-string">'zod'</span>;
<span class="hljs-keyword">const</span> schema = z.object({
  email: z.string().email(),
  age: z.number().int().positive(),
});
app.post(<span class="hljs-string">'/users'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> parsed = schema.safeParse(req.body);
  <span class="hljs-keyword">if</span> (!parsed.success) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">400</span>).json({ error: parsed.error });
  <span class="hljs-comment">// ...</span>
});
</code></pre>
<h3 id="heading-10-assuming-everyone-speaks-json">10. Assuming Everyone Speaks JSON</h3>
<p>Most APIs default to JSON responses-and that’s fine. But <strong>assuming</strong> it’s the <em>only</em> format clients ever need can be shortsighted.</p>
<pre><code class="lang-typescript">res.send(<span class="hljs-string">'Success'</span>); <span class="hljs-comment">// No content type set</span>
</code></pre>
<p>Or:</p>
<pre><code class="lang-typescript">res.json({ message: <span class="hljs-string">"Success"</span> }); <span class="hljs-comment">// even when the client asked for CSV or XML</span>
</code></pre>
<ul>
<li><p>You <strong>don’t negotiate the correct</strong> <code>Content-Type</code>, so clients may misinterpret your response.</p>
</li>
<li><p>Some clients (like CLI tools, legacy systems, BI tools) might expect other formats like <strong>CSV</strong> or <strong>XML</strong>.</p>
</li>
<li><p>Debugging becomes painful when the frontend receives HTML error pages instead of structured JSON errors.</p>
</li>
</ul>
<p>I think requesting something apart from JSON is a rare problem but let’s consider a possible fix.</p>
<p>Utilize <code>Accept</code> and <code>Content-Type</code> headers. Check and respect the <code>Accept</code> header sent by the client. Here's a TypeScript + Express example that supports both <code>application/json</code> and <code>text/csv</code>:</p>
<pre><code class="lang-typescript">app.get(<span class="hljs-string">'/reports'</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  <span class="hljs-keyword">const</span> data = [
    { id: <span class="hljs-number">1</span>, name: <span class="hljs-string">"Hakuna"</span> },
    { id: <span class="hljs-number">2</span>, name: <span class="hljs-string">"Matata"</span> },
  ];
<span class="hljs-keyword">const</span> accept = req.headers[<span class="hljs-string">'accept'</span>];
  <span class="hljs-keyword">if</span> (accept?.includes(<span class="hljs-string">'text/csv'</span>)) {
    <span class="hljs-keyword">const</span> csv = data.map(<span class="hljs-function"><span class="hljs-params">row</span> =&gt;</span> <span class="hljs-string">`<span class="hljs-subst">${row.id}</span>,<span class="hljs-subst">${row.name}</span>`</span>).join(<span class="hljs-string">'\n'</span>);
    res.setHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'text/csv'</span>);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).send(csv);
  }
  <span class="hljs-keyword">if</span> (accept?.includes(<span class="hljs-string">'application/json'</span>) || !accept) {
    res.setHeader(<span class="hljs-string">'Content-Type'</span>, <span class="hljs-string">'application/json'</span>);
    <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">200</span>).json(data);
  }
  <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">406</span>).send(<span class="hljs-string">'Not Acceptable'</span>);
});
</code></pre>
<ul>
<li><p>Use the <code>Accept</code> header to determine what the client wants.</p>
</li>
<li><p>Return a <code>406 Not Acceptable</code> if the requested format isn't supported.</p>
</li>
<li><p>Communicate data type on both sides with <code>Content-Type</code> header.</p>
</li>
<li><p>Keep JSON as your default, but make future extensibility easy.</p>
</li>
</ul>
<h3 id="heading-one-last-thing-undocumented-apis-are-invisible">One Last Thing: Undocumented APIs Are Invisible</h3>
<p>If no one knows how to use your API, it might as well not exist.</p>
<p>Use tools like OpenAPI, Swagger UI, or Redoc to generate and maintain clear, up-to-date docs automatically.</p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>Designing a good REST API isn’t just about writing endpoints-it’s about building clear, predictable contracts between systems. These mistakes are common, but fixing them early can save you from weeks of work later.</p>
<p>Start simple. Make things explicit. Keep improving.</p>
<p><strong>Have you stumbled on any of these?</strong> Drop a comment-I’d love to hear it.</p>
]]></content:encoded></item><item><title><![CDATA[What I Learned About Caching While Building Real APIs and AI Systems]]></title><description><![CDATA[“From in-memory caching and Redis to edge AI optimizations — real-world lessons that helped me speed up systems and avoid performance bottlenecks.”

What’s the sum of all integers from 1 to 100?

If you instantly said 5050, either you’re aware of Gau...]]></description><link>https://blog.theshydev.com/what-i-learned-about-caching-while-building-real-apis-and-ai-systems</link><guid isPermaLink="true">https://blog.theshydev.com/what-i-learned-about-caching-while-building-real-apis-and-ai-systems</guid><category><![CDATA[caching]]></category><category><![CDATA[backend]]></category><category><![CDATA[software development]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[APIs]]></category><category><![CDATA[System Design]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Sat, 26 Apr 2025 17:00:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/nuc3NFB_6po/upload/98575a696b1ac97cb3b5de242d8d5842.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>“From in-memory caching and Redis to edge AI optimizations — real-world lessons that helped me speed up systems and avoid performance bottlenecks.”</em></p>
<blockquote>
<p><em>What’s the sum of all integers from 1 to 100?</em></p>
</blockquote>
<p>If you instantly said 5050, either you’re aware of Gauss Summation… Or you remembered it from the last time someone asked you.</p>
<p>Now imagine I ask you the same question again… Would you calculate it again or just reuse the answer?</p>
<h3 id="heading-what-is-caching">What Is Caching?</h3>
<p><img src="https://cdn-images-1.medium.com/max/1080/0*TnjAcVStoDhEyJt0" alt /></p>
<p>Photo by <a target="_blank" href="https://unsplash.com/@liam_1?utm_source=medium&amp;utm_medium=referral">Liam Briese</a> on <a target="_blank" href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></p>
<p>Caching is the practice of <strong>temporarily storing the result of expensive operations</strong> so you can serve future requests faster without doing the same work again.</p>
<p>In REST API development, this often means <strong>avoiding unnecessary database queries</strong> or <strong>redundant API calls</strong>.</p>
<h3 id="heading-why-is-caching-important">Why Is Caching Important?</h3>
<p>Imagine your <code>/api/products</code> endpoint hits the database every time someone visits your e-commerce homepage. If 10,000 users hit that in a minute, your DB is going to suffer. With caching, you store the result once and reuse it, saving time and resources.</p>
<h3 id="heading-where-can-you-apply-caching">Where Can You Apply Caching?</h3>
<ol>
<li><p><strong>In-memory</strong> (e.g. <code>node-cache</code>, <code>memory-cache</code>)</p>
</li>
<li><p><strong>Distributed</strong> (e.g. Redis, Memcached)</p>
</li>
<li><p><strong>Browser-level</strong> (Cache-Control headers)</p>
</li>
<li><p><strong>CDNs</strong> (Cloudflare, Akamai, etc.)</p>
</li>
</ol>
<p>Let’s focus on <strong>in-memory</strong> and <strong>Redis</strong> for API development in this article. I can write posts on others if you want me to.</p>
<h3 id="heading-in-memory-cache">In-Memory Cache</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> NodeCache <span class="hljs-keyword">from</span> <span class="hljs-string">'node-cache'</span>;
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">new</span> NodeCache({ stdTTL: <span class="hljs-number">60</span> }); <span class="hljs-comment">// time to live in seconds. After that, cache expires.</span>
app.get(<span class="hljs-string">'/api/products'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> cacheKey = <span class="hljs-string">'products'</span>;
  <span class="hljs-keyword">const</span> cachedData = cache.get(cacheKey);
  <span class="hljs-keyword">if</span> (cachedData) {
    <span class="hljs-keyword">return</span> res.json({ source: <span class="hljs-string">'cache'</span>, data: cachedData });
  }
  <span class="hljs-keyword">const</span> products = <span class="hljs-keyword">await</span> fetchProductsFromDB(); <span class="hljs-comment">// simulate DB query</span>
  cache.set(cacheKey, products);
  res.json({ source: <span class="hljs-string">'db'</span>, data: products });
});
</code></pre>
<h3 id="heading-redis-cache">Redis Cache</h3>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> Redis <span class="hljs-keyword">from</span> <span class="hljs-string">'ioredis'</span>;
<span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> redis = <span class="hljs-keyword">new</span> Redis();
app.get(<span class="hljs-string">'/api/users'</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> cacheKey = <span class="hljs-string">'users'</span>;
  <span class="hljs-keyword">const</span> cached = <span class="hljs-keyword">await</span> redis.get(cacheKey);
  <span class="hljs-keyword">if</span> (cached) {
    <span class="hljs-keyword">return</span> res.json({ source: <span class="hljs-string">'redis'</span>, data: <span class="hljs-built_in">JSON</span>.parse(cached) });
  }
  <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> fetchUsersFromDB();
  <span class="hljs-keyword">await</span> redis.set(cacheKey, <span class="hljs-built_in">JSON</span>.stringify(users), <span class="hljs-string">'EX'</span>, <span class="hljs-number">120</span>); <span class="hljs-comment">// expires in 120 seconds</span>
  res.json({ source: <span class="hljs-string">'db'</span>, data: users });
});
</code></pre>
<h3 id="heading-when-you-should-and-shouldnt-cache">When You Should (and Shouldn’t) Cache?</h3>
<ul>
<li><p>✅ <strong>Data doesn’t change often</strong></p>
</li>
<li><p>✅ <strong>Reads &gt; Writes</strong></p>
</li>
<li><p>❌ <strong>Highly dynamic or user-specific data</strong> (e.g. shopping cart)</p>
</li>
</ul>
<h3 id="heading-cache-invalidation-the-hard-part">Cache Invalidation — The Hard Part</h3>
<blockquote>
<p>“There are only two hard things in computer science: cache invalidation and naming things. — Phil Karlton”</p>
</blockquote>
<p>Cache invalidation is hard, but picking the right strategy makes it manageable. You’ll need a strategy to <strong>refresh or clear the cache</strong> when your underlying data changes.</p>
<p>I’ll make a detailed post on Caching and invalidation in the coming days. Here’s a summary for now.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1745685205285/de541c94-4128-4085-b9d5-90344475f03f.png" alt /></p>
<h3 id="heading-a-classic-edge-ai-use-case">A Classic Edge AI use case</h3>
<p><strong>Caching works beyond web and APIs</strong></p>
<p>Before the medium-high powered Jetson devices era, the most common Edge AI use cases were on resource-constrained environments (Raspberry Pi, or embedded devices), where <strong>each CPU cycle is crucial</strong> for performance and energy efficiency.</p>
<p>Here’s a simple example in C++ that demonstrates caching <strong>inference results</strong> of a vision model (like object detection) for <strong>frames with little to no change</strong>, which is a common optimization.</p>
<p>Imagine you’re running a pressure gauge detection model on a camera feed. If two consecutive frames are <strong>nearly identical</strong>, you can <strong>skip re-running inference</strong> and <strong>reuse the previous result</strong> — that’s caching at the edge.</p>
<p>We’ll cache the last processed frame’s hash and its detection result.<br />If the new frame is “similar enough”, we’ll skip the model inference and reuse the result. On the edge, skipping AI inference even for a few frames saves CPU/GPU, battery, and latency.</p>
<pre><code class="lang-cpp"><span class="hljs-comment">// frame_handler.cpp</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;opencv2/opencv.hpp&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;unordered_map&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string&gt;</span></span>
cv::Mat last_frame;
<span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> last_result;
<span class="hljs-keyword">int</span> frame_threshold = <span class="hljs-number">10</span>; <span class="hljs-comment">// pixel-wise threshold for frame difference</span>
<span class="hljs-comment">// Simulate an expensive model inference</span>
<span class="hljs-function"><span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> <span class="hljs-title">runInference</span><span class="hljs-params">(<span class="hljs-keyword">const</span> cv::Mat&amp; frame)</span> </span>{
    <span class="hljs-comment">// Pretend this runs a heavy AI model</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Normal readings detected\n"</span>;
}
<span class="hljs-comment">// Compare two frames for similarity</span>
<span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">isSimilar</span><span class="hljs-params">(<span class="hljs-keyword">const</span> cv::Mat&amp; a, <span class="hljs-keyword">const</span> cv::Mat&amp; b, <span class="hljs-keyword">int</span> threshold)</span> </span>{
    <span class="hljs-keyword">if</span> (a.empty() || b.empty()) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    cv::Mat diff;
    cv::absdiff(a, b, diff);
    <span class="hljs-keyword">return</span> (cv::countNonZero(diff &gt; threshold) &lt; <span class="hljs-number">1000</span>); <span class="hljs-comment">// heuristic</span>
}
<span class="hljs-function"><span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> <span class="hljs-title">processFrame</span><span class="hljs-params">(<span class="hljs-keyword">const</span> cv::Mat&amp; frame)</span> </span>{
    <span class="hljs-keyword">if</span> (isSimilar(frame, last_frame, frame_threshold)) {
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Cache hit: Reusing previous result\n"</span>;
        <span class="hljs-keyword">return</span> last_result;
    }
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Cache miss: Running inference\n"</span>;
    last_result = runInference(frame);
    last_frame = frame.clone();
    <span class="hljs-keyword">return</span> last_result;
}

<span class="hljs-comment">// In action - main.cpp</span>
<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-function">cv::VideoCapture <span class="hljs-title">cap</span><span class="hljs-params">(<span class="hljs-number">0</span>)</span></span>; <span class="hljs-comment">// camera input</span>
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>) {
        cv::Mat frame;
        cap &gt;&gt; frame;

        <span class="hljs-keyword">if</span> (frame.empty()) <span class="hljs-keyword">break</span>;

        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> result = processFrame(frame);
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Result: "</span> &lt;&lt; result &lt;&lt; <span class="hljs-string">"\n"</span>;

        cv::imshow(<span class="hljs-string">"Edge AI Frame"</span>, frame);
        <span class="hljs-keyword">if</span> (cv::waitKey(<span class="hljs-number">30</span>) &gt;= <span class="hljs-number">0</span>) <span class="hljs-keyword">break</span>;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<h3 id="heading-you-use-caching-every-day">You use Caching every day</h3>
<p>Caching is everywhere. We rely on it daily without realizing it:</p>
<ul>
<li><p><strong>Mobile Apps:</strong> Ever noticed how apps like Instagram, YouTube, or Twitter load faster the second time you open them? That’s <strong>local caching</strong> at work — images, videos, and layouts are stored temporarily so the app doesn’t have to download everything again.</p>
</li>
<li><p><strong>Web Browsing:</strong> When you revisit a website, it often loads faster. That’s because <strong>your browser caches static assets</strong> like CSS, JavaScript, and images. No need to re-download the entire site on every visit.</p>
</li>
<li><p><strong>Your Brain (Seriously!):</strong> Remember the answer to <em>“What’s the sum of 1 to 100?”</em> from earlier? If you don’t recalculate it and instead recall it — that’s <strong>mental caching</strong>. Passwords, Frequently used routes, Common answers in conversations, etc. are also your cached responses.</p>
</li>
<li><p><strong>Cooking:</strong> Let’s say you prepare a big batch of curry and refrigerate it. You don’t cook it from scratch every day — you <strong>reheat</strong> it. In a sense, that’s caching in the kitchen.</p>
</li>
</ul>
<p>Caching is a <strong>universal principle</strong> of optimizing effort, whether it’s computers, phones, or your own brain.</p>
<h3 id="heading-final-thoughts">Final Thoughts</h3>
<p>Whether you’re building REST APIs, edge AI systems, or microservices — caching is one of those tools that offers <strong>massive returns for minimal effort</strong> when used right.</p>
<p>From in-memory and Redis caching to frame-level optimization in computer vision, the underlying idea is the same:<br /><strong>Don’t do expensive work more than once if you can avoid it.</strong></p>
<p>If you’re just getting started, pick one high-traffic endpoint or heavy computation, add caching, and measure the impact. You’ll be surprised how much faster and leaner your system feels.</p>
<p><strong>Have you implemented caching in your systems?</strong></p>
<ul>
<li><p>What strategies worked best for your use case?</p>
</li>
<li><p>Have you ever struggled with stale data or cache invalidation?</p>
</li>
<li><p>Any fun edge caching hacks you’ve tried?</p>
</li>
</ul>
<p>Drop your thoughts, war stories, or questions in the comments — let’s share notes and level up together.</p>
]]></content:encoded></item><item><title><![CDATA[The REST API Maturity Curve: From Junior to Tech Lead]]></title><description><![CDATA[“How your approach to API design evolves as your engineering responsibilities grow”


Photo by RealToughCandy.com: https://www.pexels.com/photo/programmer-holding-a-paper-cutout-with-an-api-quote-11035364/
“The integration worked in staging, but blew...]]></description><link>https://blog.theshydev.com/the-rest-api-maturity-curve-from-junior-to-tech-lead</link><guid isPermaLink="true">https://blog.theshydev.com/the-rest-api-maturity-curve-from-junior-to-tech-lead</guid><category><![CDATA[APIs]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[backend]]></category><category><![CDATA[software development]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Shy Dev]]></dc:creator><pubDate>Sat, 26 Apr 2025 16:17:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/OqtafYT5kTw/upload/e80f158f6b9bcc8f6cf935444f664d83.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>“How your approach to API design evolves as your engineering responsibilities grow”</p>
<p><img src="https://www.pexels.com/photo/programmer-holding-a-paper-cutout-with-an-api-quote-11035364/" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn-images-1.medium.com/max/1080/1*PIG34oqcae3nHax24_Xjhw.jpeg" alt /></p>
<p>Photo by RealToughCandy.com: <a target="_blank" href="https://www.pexels.com/photo/programmer-holding-a-paper-cutout-with-an-api-quote-11035364/">https://www.pexels.com/photo/programmer-holding-a-paper-cutout-with-an-api-quote-11035364/</a></p>
<p><strong>“The integration worked in staging, but blew up in production.”</strong></p>
<p><strong>“The client relied on a field we never intended to expose.”</strong></p>
<p><strong>“We added a small change and broke three downstream systems.”</strong></p>
<p>The above cases can be quite common when you’re primary work involves a lot of API development. In real-world engineering, <em>how</em> you understand API requirements is just as important as <em>what</em> the requirements say.</p>
<p>I intend to break down the evolution of API understanding through three lenses — <strong>Junior</strong>, <strong>Senior</strong>, and <strong>Tech Lead</strong> — based on how I’ve seen and experienced these roles.</p>
<h3 id="heading-read-this-before-going-further">Read this before going further</h3>
<ul>
<li><p><strong>Titles don’t have a real value unless the associated responsibilities are truly realized</strong>. The post is not intended to talk about individual’s performance.</p>
</li>
<li><p><strong>Code snippets written here are not tested</strong>. They are just there to explain a point visually.</p>
</li>
<li><p>I’m not explaining the most common problems at each level and fixes. <strong>The main idea is to share my interpretation of these roles.</strong></p>
</li>
</ul>
<h3 id="heading-what-do-api-requirements-actually-mean">What Do “API Requirements” Actually Mean?</h3>
<p>For many, API requirements = endpoint + method + payload + response. That’s the bare minimum. In practice, it includes:</p>
<ul>
<li><p><strong>Functional constraints</strong>: What the API must do.</p>
</li>
<li><p><strong>Contextual expectations</strong>: Why it exists, who uses it, where it fits in the system.</p>
</li>
<li><p><strong>Systemic qualities</strong>: Performance, rate limits, error semantics, observability.</p>
</li>
<li><p><strong>Temporality</strong>: What breaks if we change it? Is it versioned? Is it backward-compatible?</p>
</li>
</ul>
<p>API endpoints can be assumed like contracts for communication.</p>
<h3 id="heading-junior-engineer-fulfilling-the-contract">Junior Engineer: Fulfilling the Contract</h3>
<p>At this stage, you’re mostly writing code as instructed or requested — follow the provided Jira ticket or relevant extract from a big spec document.</p>
<p><strong>Typical focus areas:</strong></p>
<ul>
<li><p>HTTP verb and route structure</p>
</li>
<li><p>Payload fields and response types</p>
</li>
<li><p>Return correct status codes</p>
</li>
<li><p>Implement/update the unit tests</p>
</li>
</ul>
<p><strong>Where things go wrong:</strong></p>
<ul>
<li><p>Incomplete requirements from your source</p>
</li>
<li><p>Misunderstand optional vs. required fields</p>
</li>
<li><p>Don’t consider how the API is used downstream (e.g. other systems, third-party clients)</p>
</li>
<li><p>Assume “happy path” is sufficient</p>
</li>
</ul>
<p><strong>How I tried to improve</strong></p>
<p>A simple question and a perspective shift helped me improve my solution capability to implement a better solution.</p>
<blockquote>
<p>What assumptions were made for this task? (or spec?) <em>and</em> reading the API spec like a consumer, not just a coder</p>
</blockquote>
<h3 id="heading-senior-engineer-seeing-beyond-the-contract">Senior Engineer: Seeing Beyond the Contract</h3>
<p>Now you’re thinking about how this API behaves <em>in production</em>, not just if it <em>works.</em></p>
<p><strong>Your questions evolve:</strong></p>
<ul>
<li><p>How do we handle edge cases (e.g. deleted users, archived states)?</p>
</li>
<li><p>Is this exposing sensitive or inconsistent data?</p>
</li>
<li><p>How does this interact with pagination, search, localization?</p>
</li>
<li><p>Should we debounce calls or cache on client/server?</p>
</li>
</ul>
<p><strong>A problem scenario:</strong><br />Here’s something that illustrates a common real-world problem with leaking internal fields in an API response. You once exposed a <code>GET /users/:id</code> that returned full user objects.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// user.model.ts (Prisma ORM model or internal DB schema)</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.findUnique({ where: { id } });
res.json(user); <span class="hljs-comment">// Assume this exposes everything, including sensitive/internal fields like isAdmin, lastLoginIp.</span>
</code></pre>
<p>Fields like <code>isAdmin</code>, <code>lastLoginIp</code> are unintentionally exposed. If another team starts depending on them, changing those fields later becomes a breaking change.</p>
<p>A common fix to it is using DTOs and field-level control.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// dto/PublicUserDTO.ts</span>
<span class="hljs-keyword">import</span> { Exclude, Expose } <span class="hljs-keyword">from</span> <span class="hljs-string">"class-transformer"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> PublicUserDTO {
  <span class="hljs-meta">@Expose</span>()
  id: <span class="hljs-built_in">string</span>;
  <span class="hljs-meta">@Expose</span>()
  name: <span class="hljs-built_in">string</span>;
  <span class="hljs-meta">@Expose</span>()
  avatarUrl: <span class="hljs-built_in">string</span>;
  <span class="hljs-meta">@Expose</span>()
  joinedAt: <span class="hljs-built_in">Date</span>;
  <span class="hljs-meta">@Exclude</span>()
  isAdmin: <span class="hljs-built_in">boolean</span>;
  <span class="hljs-meta">@Exclude</span>()
  lastLoginIp: <span class="hljs-built_in">string</span>;
  <span class="hljs-keyword">constructor</span>(<span class="hljs-params">partial: Partial&lt;PublicUserDTO&gt;</span>) {
    <span class="hljs-built_in">Object</span>.assign(<span class="hljs-built_in">this</span>, partial);
  }
}

<span class="hljs-comment">// ---------------------------------------------------------</span>
<span class="hljs-comment">// user.controller.ts</span>
<span class="hljs-keyword">import</span> { plainToInstance } <span class="hljs-keyword">from</span> <span class="hljs-string">"class-transformer"</span>;

app.get(<span class="hljs-string">"/users/:id"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> prisma.user.findUnique({ where: { id: req.params.id } });
  <span class="hljs-keyword">if</span> (!user) <span class="hljs-keyword">return</span> res.status(<span class="hljs-number">404</span>).json({ message: <span class="hljs-string">"User not found"</span> });
  <span class="hljs-keyword">const</span> publicUser = plainToInstance(PublicUserDTO, user, {
    excludeExtraneousValues: <span class="hljs-literal">true</span>,
  });
  res.json(publicUser); <span class="hljs-comment">// Only exposes whitelisted fields</span>
});
</code></pre>
<ul>
<li><p>This keeps your <strong>public API contract stable</strong>, even as your internal models evolve.</p>
</li>
<li><p>Adding/removing fields becomes a <strong>controlled change</strong>, not a surprise.</p>
</li>
</ul>
<blockquote>
<p>Never forget that if it’s in the API, someone will depend on it. Consider minimal responses from the early stages. Expand as required after careful data-driven decisions</p>
</blockquote>
<h3 id="heading-tech-lead-designing-for-time-teams-and-trade-offs">Tech Lead: Designing for Time, Teams, and Trade-offs</h3>
<p>At this level, you’re no longer just shipping endpoints; you’re shaping <strong>how systems communicate, evolve, and scale across teams</strong>.</p>
<p><strong>Your focus shifts:</strong></p>
<ul>
<li><p>Will adding a required query param break downstream consumers?</p>
</li>
<li><p>Does this align with the REST patterns used across other teams?</p>
</li>
<li><p>What happens if a dependent service goes down mid-request?</p>
</li>
<li><p>Can we detect and respond to API degradation before users feel it?</p>
</li>
<li><p>Are we truly seeing the consumer perspective?</p>
</li>
</ul>
<p>Often, the most important API requirements are the ones <strong>not written down</strong>:</p>
<ul>
<li><p>What are clients assuming implicitly?</p>
</li>
<li><p>Is this endpoint used synchronously or in batch mode?</p>
</li>
<li><p>Is this API part of a contract with an external partner?</p>
</li>
<li><p>Will this be consumed by mobile apps (with bad network)?</p>
</li>
</ul>
<p>You learn to <em>ask better questions</em>:</p>
<ul>
<li><p>“Why does this need to be real-time?”</p>
</li>
<li><p>“What happens if the field is missing?”</p>
</li>
<li><p>“Who else might be affected if we change this?”</p>
</li>
<li><p>“How frequently will the user call this?”</p>
</li>
</ul>
<p>You’re no longer just writing code-you’re <strong>designing long-term contracts</strong>. And often, the hardest work is <strong>navigating ambiguity</strong>, aligning cross-team needs, and making intentional trade-offs.</p>
<p><strong>A problem scenario:</strong><br />You’re building a <strong>User Profile API</strong> used by internal HR tools, customer-facing dashboards, and reporting systems. Each use case needs different slices of data, and some rely on slow or failure-prone downstream services.</p>
<blockquote>
<p><em>Do you build one endpoint to serve all? Or a clean core endpoint with sidecar APIs?</em></p>
</blockquote>
<p>Initially, you build a single endpoint to serve all needs-but it quickly becomes fragile and hard to evolve.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// One Endpoint Trying to Serve All Use Cases</span>
app.get(<span class="hljs-string">"/api/v3/users/:userId"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  <span class="hljs-keyword">const</span> user = <span class="hljs-keyword">await</span> profileService.fetch(userId);
<span class="hljs-keyword">const</span> location = <span class="hljs-keyword">await</span> geoService.lookup(user.locationId); <span class="hljs-comment">// Assuming it's a 3rd party service, it might fail or be slow</span>
  <span class="hljs-keyword">const</span> adminFlags = <span class="hljs-keyword">await</span> hrService.getAdminFlags(user.id); <span class="hljs-comment">// Not needed by all consumers</span>
  res.json({
    id: user.id,
    name: <span class="hljs-string">`<span class="hljs-subst">${user.firstName}</span> <span class="hljs-subst">${user.lastName}</span>`</span>,
    email: user.email,
    location,
    adminFlags,
  });
});
</code></pre>
<p>The above code is fragile. A single service failure takes down the entire response. Everyone gets all the data, whether they need it or not.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// DTO + Fallbacks is one way to solve this problem</span>
<span class="hljs-comment">// dto/PublicUserProfileDTO.ts</span>
<span class="hljs-keyword">import</span> { Expose } <span class="hljs-keyword">from</span> <span class="hljs-string">"class-transformer"</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> PublicUserProfileDTO {
  <span class="hljs-meta">@Expose</span>() id: <span class="hljs-built_in">string</span>;
  <span class="hljs-meta">@Expose</span>() name: <span class="hljs-built_in">string</span>;
  <span class="hljs-meta">@Expose</span>() avatarUrl: <span class="hljs-built_in">string</span>;
  <span class="hljs-meta">@Expose</span>() location?: <span class="hljs-built_in">string</span>;
  <span class="hljs-keyword">static</span> <span class="hljs-keyword">from</span>(user: <span class="hljs-built_in">any</span>, location?: <span class="hljs-built_in">string</span>): PublicUserProfileDTO {
    <span class="hljs-keyword">return</span> {
      id: user.id,
      name: <span class="hljs-string">`<span class="hljs-subst">${user.firstName}</span> <span class="hljs-subst">${user.lastName}</span>`</span>,
      avatarUrl: user.avatarUrl,
      location,
    };
  }
}
<span class="hljs-comment">// ---------------------------------------------------------</span>
<span class="hljs-comment">// user.controller.ts</span>
<span class="hljs-keyword">import</span> { plainToInstance } <span class="hljs-keyword">from</span> <span class="hljs-string">"class-transformer"</span>;
<span class="hljs-keyword">import</span> { PublicUserProfileDTO } <span class="hljs-keyword">from</span> <span class="hljs-string">"../dto/PublicUserProfileDTO"</span>;
app.get(<span class="hljs-string">"/api/v3/users/:userId"</span>, <span class="hljs-keyword">async</span> (req, res) =&gt; {
  onst user = <span class="hljs-keyword">await</span> profileService.fetch(req.params.userId);
  <span class="hljs-keyword">const</span> role = req.user?.role || <span class="hljs-string">"public"</span>; <span class="hljs-comment">// Confirm the role</span>

  <span class="hljs-keyword">if</span> (role === <span class="hljs-string">"admin"</span>) {
    adminFlags = <span class="hljs-keyword">await</span> hrService.getAdminFlags(user.id);
  }
  <span class="hljs-keyword">let</span> location: <span class="hljs-built_in">string</span> | <span class="hljs-literal">undefined</span>;
  <span class="hljs-keyword">try</span> {
    location = <span class="hljs-keyword">await</span> geoService.lookup(user.locationId);
  } <span class="hljs-keyword">catch</span> {
    location = <span class="hljs-literal">undefined</span>; <span class="hljs-comment">// Fallback. dummy example. Please don't come after me saying it's against Typescript philosophy</span>
  }
  <span class="hljs-keyword">const</span> response = plainToInstance(
    PublicUserProfileDTO,
    PublicUserProfileDTO.from(user, location),
    { excludeExtraneousValues: adminFlags } <span class="hljs-comment">// Get only required content</span>
  );
  res.json(response);
});
</code></pre>
<ul>
<li><p>Only explicitly exposed fields are sent; internal schema changes won’t leak into the response.</p>
</li>
<li><p>Having a fallback ensures predictable responses if other services fail.</p>
</li>
<li><p>You can create new DTOs for future API versions without touching core logic.</p>
</li>
</ul>
<p>You’re no longer optimizing just for <strong>functionality</strong>, but for <strong>resilience, collaboration, and change over time</strong>.</p>
<p>Because at this level, the question isn’t just <em>“Does the API work?”</em>; it’s <em>“Will it still work X months/years from now, under pressure, across clients?”</em></p>
<h3 id="heading-takeaways">Takeaways</h3>
<ul>
<li><p><strong>Junior</strong> engineers think about correctness of their implementation.</p>
</li>
<li><p><strong>Seniors</strong> think about robustness and user experience.</p>
</li>
<li><p><strong>Leaders</strong> think about longevity and ecosystem alignment.</p>
</li>
<li><p>True API skill isn’t writing endpoints — it’s <em>understanding requirements in multiple dimensions</em>.</p>
</li>
</ul>
<blockquote>
<p><em>Good engineers implement APIs. Great engineers challenge the need for them.</em></p>
</blockquote>
<hr />
<p>What API lesson left an impact on you at job? Or if you’re earlier in your journey: What part of API design still feels unclear?</p>
<p>Drop a comment or share your favorite war story. Let’s grow together.</p>
<p>Until next time 👋</p>
]]></content:encoded></item></channel></rss>