<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tao Ran</title>
    <description>The latest articles on DEV Community by Tao Ran (@tarrantro).</description>
    <link>https://dev.clauneck.workers.dev/tarrantro</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3999968%2F3aa85e0f-551c-4f22-99cc-c9dbb5dc390a.jpg</url>
      <title>DEV Community: Tao Ran</title>
      <link>https://dev.clauneck.workers.dev/tarrantro</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.clauneck.workers.dev/feed/tarrantro"/>
    <language>en</language>
    <item>
      <title>AI-Assisted Development: A Practical Guide for Engineers</title>
      <dc:creator>Tao Ran</dc:creator>
      <pubDate>Wed, 24 Jun 2026 07:27:35 +0000</pubDate>
      <link>https://dev.clauneck.workers.dev/tarrantro/ai-assisted-development-a-practical-guide-for-engineers-20cf</link>
      <guid>https://dev.clauneck.workers.dev/tarrantro/ai-assisted-development-a-practical-guide-for-engineers-20cf</guid>
      <description>&lt;h2&gt;
  
  
  1. Why You Need a Configuration System
&lt;/h2&gt;

&lt;p&gt;Most developers start with Claude CLI the same way: open a terminal, ask a question, get an answer — then repeat themselves every session explaining the tech stack, coding conventions, and project structure.&lt;/p&gt;

&lt;p&gt;The root cause is missing context. &lt;strong&gt;A configuration system's core value is ensuring the AI already understands your project, your preferences, and what it's allowed to do before a single message is typed.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A well-structured configuration lets you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give the AI permanent knowledge of your tech stack and conventions, no re-explaining needed&lt;/li&gt;
&lt;li&gt;Require explicit confirmation before dangerous operations like &lt;code&gt;git push --force&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Automate repetitive tasks like formatting on file save&lt;/li&gt;
&lt;li&gt;Share a consistent AI experience across your entire team&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. File Hierarchy Overview
&lt;/h2&gt;

&lt;p&gt;Claude CLI configuration follows a &lt;strong&gt;proximity principle&lt;/strong&gt;: configuration closer to the project takes higher priority. Personal settings never bleed into shared team configuration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.claude/                          # Global user-level config
├── settings.json                   # Global permissions, model, behavior
├── CLAUDE.md                       # Personal global preferences (not committed)
├── keybindings.json                # Custom keyboard shortcuts
└── projects/
    └── &amp;lt;project-hash&amp;gt;/
        └── memory/                 # Auto-generated project memory files

&amp;lt;project-root&amp;gt;/
├── CLAUDE.md                       # Project-level briefing (committed, team-shared)
├── .claude/
│   ├── settings.json               # Project permissions and behavior (committed)
│   ├── settings.local.json         # Local overrides (add to .gitignore)
│   └── skills/                     # Project-specific Skill definitions
│       └── my-skill.md
└── src/
    └── components/
        └── CLAUDE.md               # Subdirectory-level notes (optional)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configuration Priority (highest to lowest)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Subdirectory CLAUDE.md
  &amp;gt; Project .claude/settings.local.json
    &amp;gt; Project .claude/settings.json
      &amp;gt; Project root CLAUDE.md
        &amp;gt; Global ~/.claude/settings.json
          &amp;gt; Global ~/.claude/CLAUDE.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt;: team conventions go in project-level config, personal preferences go in global config, sensitive local overrides go in &lt;code&gt;settings.local.json&lt;/code&gt; (gitignored).&lt;/p&gt;




&lt;h2&gt;
  
  
  3. CLAUDE.md: Your Project's AI Briefing Document
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; is automatically loaded by Claude CLI at the start of every conversation. Think of it as an onboarding document the AI reads before touching your codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a Good CLAUDE.md Contains
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Project Name&lt;/span&gt;

&lt;span class="gu"&gt;## Tech Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Backend: Go 1.22, Chi router
&lt;span class="p"&gt;-&lt;/span&gt; Frontend: React 18 + TypeScript, Vite
&lt;span class="p"&gt;-&lt;/span&gt; Database: PostgreSQL 15, sqlc for type-safe queries
&lt;span class="p"&gt;-&lt;/span&gt; Testing: Go stdlib testing + testify; Vitest for frontend

&lt;span class="gu"&gt;## Project Structure&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`cmd/`&lt;/span&gt; — service entry points
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`internal/`&lt;/span&gt; — business logic (no external imports)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`pkg/`&lt;/span&gt; — reusable libraries
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`web/`&lt;/span&gt; — frontend source

&lt;span class="gu"&gt;## Development Commands&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`make dev`&lt;/span&gt; — start dev environment (requires Docker)
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`make test`&lt;/span&gt; — run all tests
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`make lint`&lt;/span&gt; — run golangci-lint + eslint
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`make generate`&lt;/span&gt; — regenerate sqlc and protobuf files

&lt;span class="gu"&gt;## Coding Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Go: all errors must be handled, no bare &lt;span class="sb"&gt;`panic`&lt;/span&gt;, functions over 80 lines need splitting
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript: no &lt;span class="sb"&gt;`any`&lt;/span&gt;, functional components, state via Zustand
&lt;span class="p"&gt;-&lt;/span&gt; Commit messages follow Conventional Commits
&lt;span class="p"&gt;-&lt;/span&gt; All public APIs require integration test coverage

&lt;span class="gu"&gt;## Important Constraints&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Database migration files are immutable — only add new ones, never edit existing
&lt;span class="p"&gt;-&lt;/span&gt; Changes to &lt;span class="sb"&gt;`internal/auth`&lt;/span&gt; require a security review
&lt;span class="p"&gt;-&lt;/span&gt; Never modify anything under &lt;span class="sb"&gt;`vendor/`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Subdirectory CLAUDE.md
&lt;/h3&gt;

&lt;p&gt;For complex projects, add scoped instructions closer to specific areas of the codebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# web/src/components/CLAUDE.md&lt;/span&gt;

&lt;span class="gu"&gt;## Component Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; All components typed as &lt;span class="sb"&gt;`React.FC&amp;lt;Props&amp;gt;`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Props interface must match the component name, e.g. &lt;span class="sb"&gt;`ButtonProps`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use CSS Modules; filename must match component name
&lt;span class="p"&gt;-&lt;/span&gt; Never call APIs directly inside components — use hooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠️ &lt;strong&gt;What Should NOT Be in CLAUDE.md&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Secrets, tokens, or passwords&lt;/strong&gt; — even for development environments&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Large code samples&lt;/strong&gt; — they consume context; the AI reads source files more accurately anyway&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Frequently changing information&lt;/strong&gt; — like current version numbers; stale data misleads the AI&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. settings.json: Behavior and Permission Control
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;settings.json&lt;/code&gt; defines what Claude CLI can and cannot do. This is the core of your &lt;strong&gt;security boundary&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Permission Model
&lt;/h3&gt;

&lt;p&gt;Claude CLI uses an allowlist model: everything requires confirmation by default, and explicitly allowed operations run automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npm run *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(make *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git status)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git diff *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git log *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git add *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git commit *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Read(**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Edit(**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Write(src/**)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push --force*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Bash(rm -rf *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"Write(.env*)"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tool Permission Syntax
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ToolName(argument-match-pattern)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Bash(npm *)&lt;/code&gt; — allow all npm commands&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Bash(npm run test)&lt;/code&gt; — allow only this exact command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Read(**)&lt;/code&gt; — allow reading any file (&lt;code&gt;**&lt;/code&gt; matches multiple path segments)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Write(src/**)&lt;/code&gt; — allow writing only inside the &lt;code&gt;src/&lt;/code&gt; directory&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Model and Behavior Configuration
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"model"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"claude-opus-4-7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"NODE_ENV"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"LOG_LEVEL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"debug"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Team Config vs Personal Config
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Commit to git&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Shared team permission baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No (gitignore)&lt;/td&gt;
&lt;td&gt;Personal local overrides&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;Cross-project personal defaults&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  5. Hooks: The Key to Automated Workflows
&lt;/h2&gt;

&lt;p&gt;Hooks are shell commands configured in &lt;code&gt;settings.json&lt;/code&gt; that run automatically when specific events fire. They are the primary mechanism for automating your AI workflow.&lt;/p&gt;

&lt;h3&gt;
  
  
  Available Hook Events
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Event&lt;/th&gt;
&lt;th&gt;Fires When&lt;/th&gt;
&lt;th&gt;Common Uses&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PreToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before the AI calls a tool&lt;/td&gt;
&lt;td&gt;Block dangerous operations, audit logging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PostToolUse&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the AI calls a tool&lt;/td&gt;
&lt;td&gt;Validate results, trigger follow-up actions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Notification&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When the AI needs to alert the user&lt;/td&gt;
&lt;td&gt;Desktop notifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Stop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When the AI finishes a response turn&lt;/td&gt;
&lt;td&gt;Run tests, generate summaries&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Practical Hook Examples
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Auto-format after AI edits a file&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PostToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Edit|Write"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"if [[ &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_TOOL_INPUT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; == *.go ]]; then gofmt -w &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_TOOL_INPUT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;; fi"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Block writes to production config&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"PreToolUse"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"matcher"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write|Edit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$CLAUDE_TOOL_INPUT&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -q 'config/production' &amp;amp;&amp;amp; echo 'Direct modification of production config is not allowed' &amp;amp;&amp;amp; exit 2 || exit 0"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Desktop notification when AI finishes&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"Stop"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"notify-send 'Claude' 'Task complete'"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Hook Exit Code Convention
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Exit Code&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Proceed normally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Log a warning, proceed anyway&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Block the operation; AI receives an error message&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  6. MCP Servers: Extending AI's Reach
&lt;/h2&gt;

&lt;p&gt;MCP (Model Context Protocol) is the standard protocol for connecting AI to external systems. Through MCP Servers, the AI can query databases, call internal APIs, and interact with third-party services.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring MCP Servers
&lt;/h3&gt;

&lt;p&gt;Declare them in &lt;code&gt;settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"postgres"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-postgres"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"POSTGRES_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"postgresql://localhost/mydb"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@modelcontextprotocol/server-github"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"GITHUB_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"${GITHUB_TOKEN}"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"internal-api"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;".claude/mcp/internal-api-server.py"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"API_BASE_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://localhost:8080"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Security Principles
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reference sensitive tokens via environment variables (&lt;code&gt;${VAR_NAME}&lt;/code&gt;), never hardcode them&lt;/li&gt;
&lt;li&gt;Internal MCP Servers should default to read-only; expose write operations as explicit tools with permission controls&lt;/li&gt;
&lt;li&gt;Document each MCP Server's purpose in &lt;code&gt;CLAUDE.md&lt;/code&gt; so the AI uses them intentionally&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  7. Skills: Encapsulating Reusable AI Workflows
&lt;/h2&gt;

&lt;p&gt;Skills are structured instruction sets stored in &lt;code&gt;.md&lt;/code&gt; files and triggered via &lt;code&gt;/skill-name&lt;/code&gt; commands. They are the primary tool for &lt;strong&gt;standardizing AI workflows across a team&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Skill File Structure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# .claude/skills/create-feature.md&lt;/span&gt;
&lt;span class="p"&gt;
---&lt;/span&gt;
name: create-feature
description: Scaffold a standard feature module (handler + service + repo + tests)
triggers:
&lt;span class="gh"&gt;  - /create-feature
---
&lt;/span&gt;
&lt;span class="gu"&gt;## Usage&lt;/span&gt;

/create-feature &lt;span class="nt"&gt;&amp;lt;feature-name&amp;gt;&lt;/span&gt; [--module &lt;span class="nt"&gt;&amp;lt;module-name&amp;gt;&lt;/span&gt;]

&lt;span class="gu"&gt;## Steps&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Under &lt;span class="sb"&gt;`internal/&amp;lt;module&amp;gt;/`&lt;/span&gt;, create these files:
&lt;span class="p"&gt;   -&lt;/span&gt; &lt;span class="sb"&gt;`handler.go`&lt;/span&gt; — HTTP layer
&lt;span class="p"&gt;   -&lt;/span&gt; &lt;span class="sb"&gt;`service.go`&lt;/span&gt; — business logic layer
&lt;span class="p"&gt;   -&lt;/span&gt; &lt;span class="sb"&gt;`repo.go`&lt;/span&gt; — data access layer
&lt;span class="p"&gt;   -&lt;/span&gt; &lt;span class="sb"&gt;`*_test.go`&lt;/span&gt; — unit tests for each
&lt;span class="p"&gt;
2.&lt;/span&gt; Register routes in the &lt;span class="sb"&gt;`RegisterRoutes`&lt;/span&gt; function in &lt;span class="sb"&gt;`handler.go`&lt;/span&gt;
&lt;span class="p"&gt;
3.&lt;/span&gt; Add dependency injection bindings in &lt;span class="sb"&gt;`internal/wire.go`&lt;/span&gt;
&lt;span class="p"&gt;
4.&lt;/span&gt; Create a database migration file if schema changes are needed

&lt;span class="gu"&gt;## Output Checklist&lt;/span&gt;

List all created files and modified files when done.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Project-Level vs Global Skills
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.claude/skills/         # Project-specific; committed to git; team-shared
~/.claude/skills/       # Personal global; reusable across all projects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's Worth Turning Into a Skill
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Code generation with a fixed output structure (CRUD modules, API endpoints)&lt;/li&gt;
&lt;li&gt;Multi-step coordinated tasks (DB migration + code generation + tests)&lt;/li&gt;
&lt;li&gt;Tasks where the team has explicit conventions (PR descriptions, review checklists)&lt;/li&gt;
&lt;li&gt;Recurring analysis tasks (dependency security audits, benchmark comparisons)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  8. Agent SDK: Building Custom AI Agents
&lt;/h2&gt;

&lt;p&gt;When the interactive Claude CLI model isn't enough, the Agent SDK lets you build purpose-built agents — for example, automated code review in CI/CD pipelines, or automated triage of monitoring alerts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Core Concept: The Agent Loop
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;anthropic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Anthropic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;tools&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;read_file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Read the contents of a project file&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_schema&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;object&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;properties&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;string&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;description&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;File path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;required&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;path&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;run_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;claude-opus-4-7&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;end_turn&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;extract_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop_reason&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;tool_use&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;tool_results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute_tools&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tool_results&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CI/CD Integration Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/ai-review.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AI Code Review&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;types&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;opened&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;synchronize&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run AI Review&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;ANTHROPIC_API_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;
          &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python .claude/agents/pr-reviewer.py ${{ github.event.pull_request.number }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Agent Design Principles
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Single responsibility&lt;/strong&gt;: each agent does one thing; split complex tasks across multiple collaborating agents&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent operations&lt;/strong&gt;: retrying a failed agent run should not produce side effects&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit logging&lt;/strong&gt;: record every tool call the agent makes for debugging&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard limits&lt;/strong&gt;: set &lt;code&gt;max_tokens&lt;/code&gt; and loop iteration caps to prevent runaway agents&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  9. Development Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  9.1 Front-Load Context Into Configuration
&lt;/h3&gt;

&lt;p&gt;Stop repeating yourself. Put recurring context into &lt;code&gt;CLAUDE.md&lt;/code&gt; once.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Project-Specific Vocabulary&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; "DTO" = Data Transfer Object, lives in &lt;span class="sb"&gt;`internal/dto/`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; "UC" = Use Case, lives in &lt;span class="sb"&gt;`internal/usecase/`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; "current PR" = changes in &lt;span class="sb"&gt;`git diff main...HEAD`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, use &lt;code&gt;/clear&lt;/code&gt; to reset the conversation context when switching to an unrelated task. Long sessions accumulate stale context that degrades output quality.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.2 Principle of Least Privilege
&lt;/h3&gt;

&lt;p&gt;Resist the temptation to give the AI broad permissions for convenience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Bad:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dangerously&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;broad&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bash(*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Write(**)"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Good:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;precise,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;intentional&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;grants&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allow"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Bash(npm run *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git add src/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Write(src/**)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Write(test/**)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Bash(git push *)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Write(.env*)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"Write(config/production*)"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.3 Progressive Automation
&lt;/h3&gt;

&lt;p&gt;Start automating low-risk operations and expand trust incrementally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phase 1: Read-only automation (file reads, code search, running tests)
     ↓
Phase 2: Local write automation (file edits, local git operations)
     ↓
Phase 3: Guarded remote operations (create PR, with confirmation required)
     ↓
Phase 4: Fully automated CI/CD flows (within clearly scoped task boundaries)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.4 Write Constrained Prompts
&lt;/h3&gt;

&lt;p&gt;Structure matters more than length. Give the AI constraints, not freedom:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Vague — produces mediocre results&lt;/span&gt;
&lt;span class="s"&gt;"Optimize this code"&lt;/span&gt;

&lt;span class="c"&gt;// Constrained — produces targeted results&lt;/span&gt;
&lt;span class="s"&gt;"Refactor the validateToken function in src/auth/middleware.go:
- Goal: extract the duplicated error handling logic
- Constraint: do not change the function signature or touch test files
- Verify: run `make test` after refactoring to confirm tests pass"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.5 Use AI for Structured Code Review
&lt;/h3&gt;

&lt;p&gt;Give the AI an explicit review mandate instead of asking it to "look at the code":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Review internal/payment/calculator.go for:
1. security — SQL injection, auth bypass, input validation
2. correctness — boundary conditions, concurrency safety, error handling
3. performance — N+1 queries, unnecessary allocations
4. conventions — compliance with project rules in CLAUDE.md

Flag each issue with a severity: critical / warning / suggestion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.6 Team Collaboration Conventions
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## What to Commit&lt;/span&gt;

Always commit:
&lt;span class="p"&gt;-&lt;/span&gt; CLAUDE.md (all levels)
&lt;span class="p"&gt;-&lt;/span&gt; .claude/settings.json
&lt;span class="p"&gt;-&lt;/span&gt; .claude/skills/&lt;span class="err"&gt;*&lt;/span&gt;.md

Never commit:
&lt;span class="p"&gt;-&lt;/span&gt; .claude/settings.local.json
&lt;span class="p"&gt;-&lt;/span&gt; Any file containing tokens or credentials
&lt;span class="p"&gt;-&lt;/span&gt; ~/.claude/ (personal global config)

&lt;span class="gu"&gt;## PR Template Additions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Did conventions change? Update CLAUDE.md.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Is this task repetitive? Consider creating a Skill.
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Do new operations need permission entries in settings.json?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  10. Common Anti-Patterns
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Using AI as a Search Engine
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Anti-pattern: asking for information with a deterministic answer&lt;/span&gt;
&lt;span class="s"&gt;"How do goroutines work?"&lt;/span&gt;

&lt;span class="c"&gt;// Better: use AI for project-specific reasoning&lt;/span&gt;
&lt;span class="s"&gt;"In internal/worker/processor.go, what data race risks should I
watch for when converting the current serial processing to concurrent?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blindly Trusting AI Output
&lt;/h3&gt;

&lt;p&gt;AI can produce incorrect code with high confidence. Code generated by AI for critical paths — authentication, payments, data migrations — &lt;strong&gt;must be human-reviewed and test-covered&lt;/strong&gt; before merging. Treat AI output as a draft, not a finished product.&lt;/p&gt;

&lt;h3&gt;
  
  
  Overloading a Single Session
&lt;/h3&gt;

&lt;p&gt;Doing too much in one session leads to context pollution — early incorrect assumptions contaminate later outputs. &lt;strong&gt;Start a new session for each independent task.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Letting AI Guess Intent
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// Anti-pattern: open-ended instruction&lt;/span&gt;
&lt;span class="s"&gt;"Add some tests to this module"&lt;/span&gt;

&lt;span class="c"&gt;// Better: specify exactly what you expect&lt;/span&gt;
&lt;span class="s"&gt;"Write unit tests for the Calculate function in internal/payment/calculator.go.
Cover: happy path, zero amount, negative amount, and unsupported currency."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ignoring the AI's Uncertainty Signals
&lt;/h3&gt;

&lt;p&gt;When the AI says "I'm not sure" or "you may want to verify this" — &lt;strong&gt;don't skip past it&lt;/strong&gt;. These signals usually mean it's working at the boundary of its knowledge or lacks context. Human intervention at these moments is far more efficient than letting the AI guess.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claude</category>
      <category>tutorial</category>
      <category>coding</category>
    </item>
    <item>
      <title>Linux Backup Made Simple: Automate Incremental System Snapshots with Restic</title>
      <dc:creator>Tao Ran</dc:creator>
      <pubDate>Wed, 24 Jun 2026 07:20:24 +0000</pubDate>
      <link>https://dev.clauneck.workers.dev/tarrantro/linux-backup-made-simple-automate-incremental-system-snapshots-with-restic-4kp9</link>
      <guid>https://dev.clauneck.workers.dev/tarrantro/linux-backup-made-simple-automate-incremental-system-snapshots-with-restic-4kp9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A single Bash script, a USB drive, and 30 seconds a day. No cloud. No subscriptions. No excuses.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You have spent months tweaking your Linux environment. The perfect i3 config, the custom kernel parameters, the Docker containers you curated one by one. Then one day — a power surge fries your NVMe. A bad &lt;code&gt;rm -rf&lt;/code&gt;. A failed &lt;code&gt;dist-upgrade&lt;/code&gt;. And it's all gone.&lt;/p&gt;

&lt;p&gt;Most Linux users either don't back up at all, or rely on manual rsync and tar commands they run twice a year. The good news: you can set up fully automated, incremental, encrypted system snapshots with a single script and a USB drive — no cloud subscriptions, no complex configuration, no vendor lock-in.&lt;/p&gt;

&lt;p&gt;In this guide I'll show you how to use &lt;strong&gt;restic&lt;/strong&gt; — an open-source backup tool with built-in deduplication, client-side encryption, and snapshot management — paired with a Bash script that handles the real-world edge cases for you: detecting external mounts to avoid wasteful backups, skipping caches that would bloat your repository, and initializing itself on first run.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Restic?
&lt;/h2&gt;

&lt;p&gt;Most Linux users fall into one of two camps: they either don't back up at all, or they use rsync / tar because that's what they have always used. Both are risky for different reasons. Here is how restic compares:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Incremental&lt;/th&gt;
&lt;th&gt;Dedup&lt;/th&gt;
&lt;th&gt;Encryption&lt;/th&gt;
&lt;th&gt;Snapshot Mgmt&lt;/th&gt;
&lt;th&gt;Restore UX&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;rsync&lt;/td&gt;
&lt;td&gt;partial&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tar&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;duplicity&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;basic&lt;/td&gt;
&lt;td&gt;slow on long chains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;borg&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;requires borg binary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;timeshift&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;no&lt;/td&gt;
&lt;td&gt;yes&lt;/td&gt;
&lt;td&gt;GUI-focused, tied to local disk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;restic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;single binary, any target&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here is why these differences matter in practice:&lt;/p&gt;

&lt;h3&gt;
  
  
  True Incremental Backups
&lt;/h3&gt;

&lt;p&gt;Restic chunks your files into content-addressed blobs. When you run a backup, it only uploads chunks it hasn't seen before. The second backup of a 200GB system where you changed three config files? It takes seconds, not hours.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Deduplication
&lt;/h3&gt;

&lt;p&gt;Have the same 2GB ISO sitting in three different directories? Restic stores it once. The same Python virtual environment duplicated across five projects? One copy. This matters especially when your USB drive is finite.&lt;/p&gt;

&lt;h3&gt;
  
  
  Client-Side Encryption
&lt;/h3&gt;

&lt;p&gt;Your backup repository is encrypted before a single byte leaves your machine. Lose the USB drive on a train? Without the password, the data is meaningless noise. Restic uses AES-256 in CTR mode with Poly1305-AES for authentication.&lt;/p&gt;

&lt;h3&gt;
  
  
  Snapshots, Not Archives
&lt;/h3&gt;

&lt;p&gt;Every backup is a named, timestamped snapshot. You can list them, diff them, mount them as a FUSE filesystem, or restore individual files from any point in time. This is closer to what you would expect from a ZFS snapshot than a tarball.&lt;/p&gt;

&lt;h3&gt;
  
  
  Single Static Binary
&lt;/h3&gt;

&lt;p&gt;No daemon. No database. No kernel module. Restic is one Go binary. Copy it to any machine and it works. This also makes it trivially deployable in rescue environments — just &lt;code&gt;scp&lt;/code&gt; the binary and go.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Script: Automation with Intelligence
&lt;/h2&gt;

&lt;p&gt;A backup tool is only as good as your willingness to run it. If it requires you to remember which directories to exclude, or mounts to skip, or flags to pass, you will eventually stop running it.&lt;/p&gt;

&lt;p&gt;The script tackles four real-world problems:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Sensible Exclusions Out of the Box
&lt;/h3&gt;

&lt;p&gt;The script ships with a curated set of exclusions that skip directories you would never want in a system backup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;EXCLUDES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /proc &lt;span class="nt"&gt;--exclude&lt;/span&gt; /sys &lt;span class="nt"&gt;--exclude&lt;/span&gt; /dev
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /tmp &lt;span class="nt"&gt;--exclude&lt;/span&gt; /run &lt;span class="nt"&gt;--exclude&lt;/span&gt; /mnt &lt;span class="nt"&gt;--exclude&lt;/span&gt; /media
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /var/tmp &lt;span class="nt"&gt;--exclude&lt;/span&gt; /lost+found &lt;span class="nt"&gt;--exclude&lt;/span&gt; /.snapshots
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'/home/*/.cache'&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'/home/*/.npm'&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'/home/*/.local/share/Trash'&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /var/cache/apt/archives
&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few of these deserve explanation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/home/*/.cache&lt;/code&gt; — This is the big one. Browser caches, Go build artifacts, pip wheels, and a thousand other things apps dump here. On a well-used system this can easily be 5–10 GB of data you will never need to restore.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/home/*/.npm&lt;/code&gt; — Node.js package cache. A single &lt;code&gt;npm install&lt;/code&gt; rebuilds it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/home/*/.local/share/Trash&lt;/code&gt; — The trash bin. You deleted it once already.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/var/cache/apt/archives&lt;/code&gt; — Downloaded &lt;code&gt;.deb&lt;/code&gt; packages. &lt;code&gt;apt update&lt;/code&gt; fetches fresh ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using bash arrays (&lt;code&gt;"${EXCLUDES[@]}"&lt;/code&gt;) instead of a plain string ensures patterns like &lt;code&gt;/home/*/.cache&lt;/code&gt; survive shell expansion intact and are interpreted correctly by restic.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Auto-Detect External Mounts
&lt;/h3&gt;

&lt;p&gt;On top of the static exclusion list, the script dynamically detects remote filesystems so you never accidentally pull a NAS into your backup. It parses &lt;code&gt;/proc/mounts&lt;/code&gt; and appends matches to the same array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; _ mp fs_type _&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fs_type&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
        &lt;/span&gt;cifs|nfs|nfs4|fuse.sshfs|fuse.rclone&lt;span class="p"&gt;)&lt;/span&gt;
            EXCLUDES+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; /proc/mounts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means you can mount and unmount NAS shares, add new ones, or switch between SMB and NFS, and the script will never accidentally pull them in. Each detected mount is logged so you can verify what was excluded.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. First Run vs. Daily Run
&lt;/h3&gt;

&lt;p&gt;The script has exactly two paths through its logic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No repository yet&lt;/strong&gt; → initialize a new encrypted restic repository on the USB drive, then run the first backup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repository exists&lt;/strong&gt; → run an incremental backup. Only changed chunks are transmitted.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No separate init step. No configuration file to maintain. Plug in the USB, run the script, and it figures out what to do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;restic init &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;restic backup / &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXCLUDES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Format Flexibility
&lt;/h3&gt;

&lt;p&gt;Three modes for different needs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./backup.sh           &lt;span class="c"&gt;# incremental — what you run every day&lt;/span&gt;
./backup.sh &lt;span class="nt"&gt;--dry&lt;/span&gt;     &lt;span class="c"&gt;# preview what would change, no data written&lt;/span&gt;
./backup.sh &lt;span class="nt"&gt;--full&lt;/span&gt;    &lt;span class="c"&gt;# force re-read of all files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--dry&lt;/code&gt; mode is particularly useful before a system upgrade — see exactly which files have changed since your last backup. The &lt;code&gt;--full&lt;/code&gt; mode is for when you suspect filesystem corruption or bit rot and want a fresh scan of every file.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Script
&lt;/h2&gt;

&lt;p&gt;Copy it, adjust the two variables at the top, and you are ready to go:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="c"&gt;# ============================================================&lt;/span&gt;
&lt;span class="c"&gt;# Restic incremental backup script — local machine&lt;/span&gt;
&lt;span class="c"&gt;# Usage: ./backup.sh          # incremental backup&lt;/span&gt;
&lt;span class="c"&gt;#        ./backup.sh --dry    # preview changes&lt;/span&gt;
&lt;span class="c"&gt;#        ./backup.sh --full   # force full scan&lt;/span&gt;
&lt;span class="c"&gt;# ============================================================&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="c"&gt;# ---- Config: customize these two lines ----&lt;/span&gt;
&lt;span class="nv"&gt;USB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/media/&lt;/span&gt;&lt;span class="nv"&gt;$USER&lt;/span&gt;&lt;span class="s2"&gt;/backup-disk"&lt;/span&gt;    &lt;span class="c"&gt;# USB mount point&lt;/span&gt;
&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;USB&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt;           &lt;span class="c"&gt;# subdirectory (unique per machine)&lt;/span&gt;

&lt;span class="nv"&gt;TIMESTAMP_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/LAST_BACKUP"&lt;/span&gt;
&lt;span class="nv"&gt;LOGFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.local/state/backup.log"&lt;/span&gt;

log&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
die&lt;span class="o"&gt;()&lt;/span&gt;  &lt;span class="o"&gt;{&lt;/span&gt; log &lt;span class="s2"&gt;"ERROR: &lt;/span&gt;&lt;span class="nv"&gt;$*&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# ---- Dependencies ----&lt;/span&gt;
&lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; restic &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1 &lt;span class="o"&gt;||&lt;/span&gt; die &lt;span class="s2"&gt;"restic not installed. Run: sudo apt install restic"&lt;/span&gt;

&lt;span class="c"&gt;# ---- USB mount check ----&lt;/span&gt;
mountpoint &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; die &lt;span class="s2"&gt;"USB not mounted: &lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# ---- Build exclude list ----&lt;/span&gt;
&lt;span class="nv"&gt;EXCLUDES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /proc &lt;span class="nt"&gt;--exclude&lt;/span&gt; /sys &lt;span class="nt"&gt;--exclude&lt;/span&gt; /dev
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /tmp &lt;span class="nt"&gt;--exclude&lt;/span&gt; /run &lt;span class="nt"&gt;--exclude&lt;/span&gt; /mnt &lt;span class="nt"&gt;--exclude&lt;/span&gt; /media
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /var/tmp &lt;span class="nt"&gt;--exclude&lt;/span&gt; /lost+found &lt;span class="nt"&gt;--exclude&lt;/span&gt; /.snapshots
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'/home/*/.cache'&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'/home/*/.npm'&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s1"&gt;'/home/*/.local/share/Trash'&lt;/span&gt;
    &lt;span class="nt"&gt;--exclude&lt;/span&gt; /var/cache/apt/archives
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Auto-detect CIFS / NFS / SSHFS external mounts&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; _ mp fs_type _&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$fs_type&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
        &lt;/span&gt;cifs|nfs|nfs4|fuse.sshfs|fuse.rclone&lt;span class="p"&gt;)&lt;/span&gt;
            EXCLUDES+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nt"&gt;--exclude&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$mp&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            log &lt;span class="s2"&gt;"Excluding external mount: &lt;/span&gt;&lt;span class="nv"&gt;$mp&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$fs_type&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
            &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; /proc/mounts

&lt;span class="c"&gt;# ---- Repo init ----&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;log &lt;span class="s2"&gt;"Repo not found, initializing restic repository..."&lt;/span&gt;
    restic init &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; die &lt;span class="s2"&gt;"Repo init failed"&lt;/span&gt;
    log &lt;span class="s2"&gt;"Repo initialized."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;log &lt;span class="s2"&gt;"Repo exists, running incremental backup."&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# ---- Arguments ----&lt;/span&gt;
&lt;span class="nv"&gt;RESTIC_ARGS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
    &lt;span class="nt"&gt;--verbose&lt;/span&gt;
    &lt;span class="nt"&gt;--limit-upload&lt;/span&gt; 50000
    &lt;span class="nt"&gt;--limit-download&lt;/span&gt; 50000
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="nt"&gt;--dry&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        log &lt;span class="s2"&gt;"=== DRY-RUN mode ==="&lt;/span&gt;
        restic backup / &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXCLUDES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESTIC_ARGS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
            &lt;span class="nt"&gt;--dry-run&lt;/span&gt;
        log &lt;span class="s2"&gt;"Dry-run complete."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
        &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="nt"&gt;--full&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        log &lt;span class="s2"&gt;"Forcing full scan."&lt;/span&gt;
        RESTIC_ARGS+&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="nt"&gt;--force&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;

&lt;span class="c"&gt;# ---- Backup ----&lt;/span&gt;
log &lt;span class="s2"&gt;"Starting backup / → &lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt; ..."&lt;/span&gt;
restic backup / &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXCLUDES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RESTIC_ARGS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOGFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# ---- Timestamp ----&lt;/span&gt;
&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="s1"&gt;'+%Y-%m-%d %H:%M:%S'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TIMESTAMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
log &lt;span class="s2"&gt;"Backup complete. Timestamp: &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TIMESTAMP_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# ---- Maintenance reminder ----&lt;/span&gt;
&lt;span class="nv"&gt;SNAP_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;restic snapshots &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'^[a-f0-9]'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"?"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
log &lt;span class="s2"&gt;"Snapshot count: &lt;/span&gt;&lt;span class="nv"&gt;$SNAP_COUNT&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
log &lt;span class="s2"&gt;"Tip: restic forget --repo &lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt; --keep-daily 14 --keep-monthly 6 --keep-yearly 5 --prune"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Step-by-Step Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Prerequisites
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;A USB drive (preferably SSD) with enough capacity. Format it as ext4 for best performance and to preserve Linux permissions.&lt;/li&gt;
&lt;li&gt;restic installed on your machine (&lt;code&gt;sudo apt install restic&lt;/code&gt; on Debian/Ubuntu).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 1: Find Your USB
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsblk &lt;span class="nt"&gt;-o&lt;/span&gt; NAME,SIZE,TYPE,MOUNTPOINT,LABEL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Look for your USB drive in the output. Note the mount point — typically &lt;code&gt;/media/$USER/&amp;lt;label&amp;gt;&lt;/code&gt;. Set the &lt;code&gt;USB&lt;/code&gt; variable in the script to match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Save the Script
&lt;/h3&gt;

&lt;p&gt;Copy the script above into a file and make it executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have multiple machines, give each a different &lt;code&gt;REPO&lt;/code&gt; name (e.g., &lt;code&gt;my-laptop&lt;/code&gt;, &lt;code&gt;my-desktop&lt;/code&gt;, &lt;code&gt;my-server&lt;/code&gt;). This keeps their backups in separate subdirectories on the same USB drive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: First Run
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./backup.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restic will prompt you to set a password for the repository. &lt;strong&gt;Do not lose this password.&lt;/strong&gt; Write it down. Store it in your password manager. Without it, the backup is unrecoverable. There is no "reset password" button.&lt;/p&gt;

&lt;p&gt;The first run will take a while — it reads your entire filesystem. On a 200GB SSD over USB 3.0, expect roughly 20–40 minutes. Subsequent runs take seconds to a few minutes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Verify
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic snapshots &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see your first snapshot, timestamped. Run &lt;code&gt;./backup.sh&lt;/code&gt; again a few hours later and you will see a second snapshot appear. The data stored on disk will barely grow because only changed files were uploaded.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5: Automate (Optional but Recommended)
&lt;/h3&gt;

&lt;p&gt;Add a cron job or systemd timer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# crontab -e&lt;/span&gt;
0 20 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /home/&lt;span class="nv"&gt;$USER&lt;/span&gt;/scripts/backup.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /home/&lt;span class="nv"&gt;$USER&lt;/span&gt;/.local/state/backup.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This runs the backup every evening at 8 PM. Since the script uses restic's &lt;code&gt;--limit-upload&lt;/code&gt; flag, it won't saturate your USB bandwidth even on slower drives.&lt;/p&gt;




&lt;h2&gt;
  
  
  Maintenance and Restore
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Cleaning Up Old Snapshots
&lt;/h3&gt;

&lt;p&gt;Snapshots accumulate. After a few months, you might have hundreds. Restic's forget policy lets you keep a sensible rotation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic forget &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-daily&lt;/span&gt; 14 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-monthly&lt;/span&gt; 6 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--keep-yearly&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--prune&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps: every snapshot from the last 14 days, one per month for the last 6 months, and one per year for the last 5 years. Everything else is pruned and the underlying data blobs are garbage-collected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Restoring Your System
&lt;/h3&gt;

&lt;p&gt;Full system restore to a new disk:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic restore latest &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; /mnt/new-disk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Single file restore:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic restore latest &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; / &lt;span class="nt"&gt;--include&lt;/span&gt; /home/&lt;span class="nv"&gt;$USER&lt;/span&gt;/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restore from a specific date:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic snapshots &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt;              &lt;span class="c"&gt;# find the snapshot ID&lt;/span&gt;
restic restore &amp;lt;snapshot-id&amp;gt; &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt; &lt;span class="nt"&gt;--target&lt;/span&gt; /path
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Checking Repository Health
&lt;/h3&gt;

&lt;p&gt;Run periodically (monthly is fine):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic check &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt;               &lt;span class="c"&gt;# quick structural check&lt;/span&gt;
restic check &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt; &lt;span class="nt"&gt;--read-data&lt;/span&gt;   &lt;span class="c"&gt;# thorough, reads all data&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Mounting Snapshots as a Filesystem
&lt;/h3&gt;

&lt;p&gt;You can browse your snapshots without restoring:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;restic mount &lt;span class="nt"&gt;--repo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$USB&lt;/span&gt;&lt;span class="s2"&gt;/my-laptop"&lt;/span&gt; /mnt/restic
&lt;span class="nb"&gt;ls&lt;/span&gt; /mnt/restic/snapshots/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is invaluable for comparing versions or grabbing a file you deleted last week without doing a full restore.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;You don't need a complex backup strategy. You need one USB drive, one script, and one habit.&lt;/p&gt;

&lt;p&gt;Restic gives you enterprise-grade backup features — deduplication, encryption, snapshot management — in a tool that is genuinely simple to use. The script wraps it in automation that handles the real-world edge cases: external mounts, cache directories, first-run initialization, and daily incrementals.&lt;/p&gt;

&lt;p&gt;Set it up once. Run it often. The next time your system breaks, you will be back up and running in the time it takes to restore a snapshot, not the time it takes to rebuild from memory.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>devops</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
