<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Helder Burato Berto</title>
    <description>The latest articles on Forem by Helder Burato Berto (@helderberto).</description>
    <link>https://forem.com/helderberto</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F37296%2F07ebbafa-745f-4daa-a91c-57d27cf472d0.jpg</url>
      <title>Forem: Helder Burato Berto</title>
      <link>https://forem.com/helderberto</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/helderberto"/>
    <language>en</language>
    <item>
      <title>Why I Built TracerKit</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Mon, 06 Apr 2026 11:20:24 +0000</pubDate>
      <link>https://forem.com/helderberto/why-i-built-tracerkit-fmo</link>
      <guid>https://forem.com/helderberto/why-i-built-tracerkit-fmo</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/helderberto/how-i-use-claude-code-to-build-features-4lbe"&gt;In a previous post&lt;/a&gt;, I described a workflow for building features with Claude Code: plan in conversation, save the plan as &lt;code&gt;SPEC.md&lt;/code&gt;, implement task by task, &lt;code&gt;/clear&lt;/code&gt; between sessions. The spec is the memory, the session is the execution.&lt;/p&gt;

&lt;p&gt;I ended that post saying the workflow would change. It did.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the Manual Workflow Broke
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;SPEC.md&lt;/code&gt; approach worked, but three problems kept surfacing:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Inconsistent specs.&lt;/strong&gt; Every feature started from scratch. Some specs had decisions sections, some didn't. Some had task checklists, others had prose. Quality depended on how disciplined I was that day.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No verification.&lt;/strong&gt; I'd mark tasks &lt;code&gt;[x]&lt;/code&gt; by hand. Sometimes before tests passed. Sometimes I'd forget to update the spec entirely. The source of truth drifted from reality.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;No lifecycle.&lt;/strong&gt; Finished specs sat in the project root next to active ones. No archive, no status tracking, no way to see what was in flight.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I kept patching these with better prompts and more discipline. Then I realized the workflow itself should be the tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow Now
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/helderberto/tracerkit" rel="noopener noreferrer"&gt;TracerKit&lt;/a&gt; is what came out of that realization. A set of AI agent skills (pure Markdown, zero runtime deps) that replace the manual spec workflow with a structured one.&lt;/p&gt;

&lt;p&gt;Here's the same dark mode feature from the &lt;a href="https://dev.to/helderberto/how-i-use-claude-code-to-build-features-4lbe"&gt;previous post&lt;/a&gt;, this time through TracerKit.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Orient
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tk:brief
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At the start of any session, this shows what's in flight:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Feature   | Status      | Age | Progress | Next                        |
|-----------|-------------|-----|----------|-----------------------------|
| dark-mode | in_progress | 1d  | 4/7      | Add accessible toggle label |

&lt;span class="gs"&gt;**Focus -&amp;gt; dark-mode**&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One command to orient. No digging through files to remember where I left off.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Define the PRD
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tk:prd enable dark mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill explores the codebase first, then interviews me one question at a time. It pushes back on vague answers, flags contradictions, and forces me to define scope boundaries before writing anything.&lt;/p&gt;

&lt;p&gt;The output is a structured PRD at &lt;code&gt;.tracerkit/prds/dark-mode.md&lt;/code&gt; with frontmatter tracking status:&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2026-04-06T10:00:00Z&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;created&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Dark Mode&lt;/span&gt;

&lt;span class="gu"&gt;## Problem Statement&lt;/span&gt;
...

&lt;span class="gu"&gt;## User Stories&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; As a user, I want to toggle between light and dark themes...

&lt;span class="gu"&gt;## Implementation Decisions&lt;/span&gt;
...

&lt;span class="gu"&gt;## Out of Scope&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every PRD follows the same structure. No more specs that vary by discipline level.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Plan in Tracer-Bullet Slices
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tk:plan dark-mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The skill reads the PRD, explores the codebase, and breaks the feature into &lt;strong&gt;tracer-bullet vertical slices&lt;/strong&gt;, a concept from &lt;em&gt;The Pragmatic Programmer&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Instead of building layer by layer (all schema, then all services, then all UI), each phase cuts a thin path through every layer and is demoable on its own. Integration problems surface in phase 1, not at the end.&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;## Phase 1 — Theme toggles end-to-end&lt;/span&gt;

&lt;span class="gu"&gt;### Done when&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] &lt;span class="sb"&gt;`useTheme`&lt;/span&gt; hook reads/writes localStorage
&lt;span class="p"&gt;-&lt;/span&gt; [ ] &lt;span class="sb"&gt;`ThemeProvider`&lt;/span&gt; applies theme class on mount
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Toggle button in Header switches theme
&lt;span class="p"&gt;-&lt;/span&gt; [ ] No flash of wrong theme on reload
&lt;span class="p"&gt;
---
&lt;/span&gt;
&lt;span class="gu"&gt;## Phase 2 — Polish and accessibility&lt;/span&gt;

&lt;span class="gu"&gt;### Done when&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] System preference used as default when no localStorage value
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Toggle has accessible label and keyboard support
&lt;span class="p"&gt;-&lt;/span&gt; [ ] All tests pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key difference from a flat task list: each phase is independently verifiable. The "Done when" items are testable conditions, not prose. An agent can verify them by reading files or running commands.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Implement Phase by Phase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@.tracerkit/plans/dark-mode.md do phase 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same as before: reference the plan, implement, run tests. But the plan has structure that survives context loss. Each phase has explicit acceptance criteria. I don't need to re-explain what "done" means.&lt;/p&gt;

&lt;p&gt;Between phases, &lt;code&gt;/clear&lt;/code&gt;. The plan is the memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Verify and Archive
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/tk:check dark-mode
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the step that didn't exist before. The skill launches a read-only review agent that checks every "Done when" item against the actual codebase. It runs the test suite, compares user stories from the PRD against behavior, and reports findings:&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;## Verification: dark-mode&lt;/span&gt;

&lt;span class="gu"&gt;### Status: done&lt;/span&gt;

&lt;span class="gu"&gt;### Progress&lt;/span&gt;
Phase 1 — Theme toggles end-to-end: 4/4
Phase 2 — Polish and accessibility: 3/3
Total: 7/7

&lt;span class="gu"&gt;### BLOCKERS&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; None

&lt;span class="gu"&gt;### SUGGESTIONS&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; None
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;done&lt;/code&gt;, it archives the PRD and plan to &lt;code&gt;.tracerkit/archives/dark-mode/&lt;/code&gt; and cleans up the active files. No stale specs sitting in the project root.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed, What Didn't
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before (manual SPEC.md)&lt;/th&gt;
&lt;th&gt;After (TracerKit)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Spec structure varies per feature&lt;/td&gt;
&lt;td&gt;Same structure every time via interview&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tasks marked done by hand&lt;/td&gt;
&lt;td&gt;Verification agent checks against codebase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Finished specs accumulate&lt;/td&gt;
&lt;td&gt;Auto-archived on completion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No cross-feature visibility&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;/tk:brief&lt;/code&gt; dashboard&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flat task lists&lt;/td&gt;
&lt;td&gt;Phased tracer-bullet slices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Decisions drift mid-session&lt;/td&gt;
&lt;td&gt;Architectural decisions locked in plan header&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What didn't change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; still defines &lt;em&gt;how&lt;/em&gt; to work. TracerKit defines &lt;em&gt;what&lt;/em&gt; to build.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/clear&lt;/code&gt; between phases. The plan is the context, not the conversation.&lt;/li&gt;
&lt;li&gt;TDD during implementation. TracerKit structures the spec work, not the coding.&lt;/li&gt;
&lt;li&gt;The spec is still a Markdown file. No database, no server, no lock-in.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core habit is the same: write down what you're building before you build it. TracerKit made the habit consistent and added the verification step I was too lazy to do manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;Same as before, with one addition:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The spec is the memory. The session is the execution. &lt;strong&gt;Verification closes the loop.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without verification, specs become wish lists. Tasks get marked done without evidence. &lt;code&gt;/tk:check&lt;/code&gt; forces honesty: either the codebase matches the plan or it doesn't.&lt;/p&gt;

&lt;p&gt;The other addition: lifecycle. Features have a clear path: &lt;code&gt;created -&amp;gt; in_progress -&amp;gt; done -&amp;gt; archived&lt;/code&gt;. At any point, &lt;code&gt;/tk:brief&lt;/code&gt; shows where everything stands.&lt;/p&gt;

&lt;p&gt;Not every task needs the full workflow. A one-line fix or a small config change doesn't need a PRD and a phased plan. Use your judgment. That said, I've had consistent success following the full flow even for tasks that seemed small at first. Small tasks have a way of growing, and the spec catches that early.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;TracerKit is open source and stack-agnostic. It currently supports Claude Code, with Cursor and Copilot on the roadmap.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; tracerkit
tracerkit init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a href="https://github.com/helderberto/tracerkit" rel="noopener noreferrer"&gt;github.com/helderberto/tracerkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The idea for TracerKit came out of &lt;a href="https://www.aihero.dev/cohorts/claude-code-for-real-engineers-2026-04" rel="noopener noreferrer"&gt;Claude Code for Real Engineers&lt;/a&gt;, a cohort by &lt;a href="https://github.com/mattpocock" rel="noopener noreferrer"&gt;Matt Pocock&lt;/a&gt;. The hands-on approach to building real things with Claude Code made the friction in my manual workflow impossible to ignore.&lt;/p&gt;

&lt;p&gt;The entire project was built using its own workflow. Dog-fooding from day one. The walkthrough above is a real example, not a demo.&lt;/p&gt;

&lt;p&gt;Ideas, feedback, and feature requests are welcome as &lt;a href="https://github.com/helderberto/tracerkit/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen" rel="noopener noreferrer"&gt;GitHub issues&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://dev.to/helderberto/how-i-use-claude-code-to-build-features-4lbe"&gt;previous workflow&lt;/a&gt; was the right instinct: structure your AI sessions around a living spec. TracerKit is what happens when you stop maintaining that structure by hand and let the tools enforce it.&lt;/p&gt;

&lt;p&gt;AI tooling will keep evolving. The habit of specifying before building won't.&lt;/p&gt;

&lt;p&gt;If you try TracerKit, I'd like to hear how it fits your workflow. Share what worked, what didn't, and what's missing. The best features so far came from real usage, not guesses. Open an &lt;a href="https://github.com/helderberto/tracerkit/issues?q=sort%3Aupdated-desc+is%3Aissue+is%3Aopen" rel="noopener noreferrer"&gt;issue&lt;/a&gt; with ideas, friction points, or your own spec-driven flow. Building in public works better when the feedback loop is open.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/how-i-use-claude-code-to-build-features-4lbe"&gt;How I Use Claude Code to Build Features&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/ai-writes-code-you-own-quality-19g0"&gt;AI Writes Code. You Own Quality.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/teaching-claude-code-your-standards-k9p"&gt;Teaching Claude Code Your Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/claude-code-hooks-1k7a"&gt;Claude Code Hooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>software</category>
      <category>development</category>
      <category>programming</category>
    </item>
    <item>
      <title>How I Use Claude Code to Build Features</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Thu, 26 Mar 2026 15:23:10 +0000</pubDate>
      <link>https://forem.com/helderberto/how-i-use-claude-code-to-build-features-4lbe</link>
      <guid>https://forem.com/helderberto/how-i-use-claude-code-to-build-features-4lbe</guid>
      <description>&lt;p&gt;This is how I'm building features with Claude Code right now. AI tooling is evolving fast and this workflow will likely change. I'll write a follow-up when it does.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;Claude Code loses coherence as context fills. Long sessions accumulate noise: early decisions get buried, the agent starts contradicting prior agreements, or repeats work already done.&lt;/p&gt;

&lt;p&gt;The instinct is to keep sessions alive to preserve context. This is backwards. A well-structured &lt;code&gt;SPEC.md&lt;/code&gt; is more reliable than conversation history, and the cost of not having one adds up fast.&lt;/p&gt;

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

&lt;p&gt;Context loss isn't just annoying. It's expensive.&lt;/p&gt;

&lt;p&gt;Without a spec, you spend sessions re-explaining what you already agreed on. Claude regenerates code it already wrote. You catch contradictions only after they're merged. A 2-hour feature stretches into 6 hours of back-and-forth.&lt;/p&gt;

&lt;p&gt;The cost compounds: you start second-guessing the AI's output, micromanaging every change, or abandoning the session entirely and starting over. The tool that should multiply your speed becomes a source of friction.&lt;/p&gt;

&lt;p&gt;A living spec eliminates this. The AI reads the same source of truth every time. You stop being a context manager and go back to being an engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Workflow
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Plan Mode First
&lt;/h3&gt;

&lt;p&gt;Before writing any code, I switch to plan mode using &lt;code&gt;Shift+Tab&lt;/code&gt; in Claude Code, then describe the feature. I end the prompt asking Claude to generate a &lt;code&gt;SPEC.md&lt;/code&gt; once we finish the planning session:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I want to add a dark mode toggle that persists across sessions.
The preference should be stored in localStorage and applied on initial render.

Let's discuss the approach, identify the files involved, and clarify any open questions.
At the end of our planning, generate a SPEC.md with the agreed context, tasks, and decisions.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@&lt;/code&gt; syntax in Claude Code lets you reference files directly in your prompt. Use it to point at existing files during planning so Claude has the right context before proposing anything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@src/components/Header.tsx @src/hooks/

I want to add a dark mode toggle to the Header.
Let's plan the approach before writing any code.
At the end, generate a SPEC.md.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plan mode keeps Claude from jumping straight to implementation. We brainstorm, I push back on things that don't fit, and only after the plan is solid does anything get written to disk.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Save the Plan as SPEC.md
&lt;/h3&gt;

&lt;p&gt;Once I'm happy with the plan, I save it to &lt;code&gt;SPEC.md&lt;/code&gt; at the project root. A task checklist is more useful than prose:&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;# Dark Mode Toggle&lt;/span&gt;

&lt;span class="gu"&gt;## Context&lt;/span&gt;

Persist user theme preference using localStorage. Apply on initial render to avoid flash.

&lt;span class="gu"&gt;## Approach&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Store preference as &lt;span class="sb"&gt;`theme`&lt;/span&gt; key in localStorage (&lt;span class="sb"&gt;`"dark"`&lt;/span&gt; | &lt;span class="sb"&gt;`"light"`&lt;/span&gt;)
&lt;span class="p"&gt;-&lt;/span&gt; Read on mount in &lt;span class="sb"&gt;`ThemeProvider`&lt;/span&gt;, default to system preference if unset
&lt;span class="p"&gt;-&lt;/span&gt; Toggle button in &lt;span class="sb"&gt;`Header`&lt;/span&gt;, updates state and localStorage in sync

&lt;span class="gu"&gt;## Tasks&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [ ] Add &lt;span class="sb"&gt;`useTheme`&lt;/span&gt; hook in &lt;span class="sb"&gt;`src/hooks/useTheme.ts`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Update &lt;span class="sb"&gt;`ThemeProvider`&lt;/span&gt; to read from localStorage on mount
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Add toggle button to &lt;span class="sb"&gt;`Header`&lt;/span&gt; component
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Write unit tests for &lt;span class="sb"&gt;`useTheme`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Write integration test for persistence across render

&lt;span class="gu"&gt;## Decisions&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; No server-side preference (client only for now)
&lt;span class="p"&gt;-&lt;/span&gt; System preference as fallback, not default dark
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;## Decisions&lt;/code&gt; section matters. Decisions made during planning get forgotten. Writing them down prevents re-litigating the same tradeoffs mid-implementation.&lt;/p&gt;

&lt;p&gt;Keep the spec to: feature context, chosen approach, task checklist, and key decisions. Implementation details belong in the code. The spec should be small enough to reread in 30 seconds.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Implement
&lt;/h3&gt;

&lt;p&gt;When starting a new session to implement, I always reference the spec explicitly using &lt;code&gt;@&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SPEC.md Continue from the next unchecked task.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;@SPEC.md&lt;/code&gt; ensures the file content is injected directly into the context. Claude isn't guessing. It has the full spec in front of it before writing a single line.&lt;/p&gt;

&lt;p&gt;Things like TDD, code style, and commit conventions are already defined in my &lt;a href="https://github.com/helderberto/dotfiles/blob/main/dot_claude/CLAUDE.md" rel="noopener noreferrer"&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/a&gt;. I don't repeat them in the spec or in the session prompt. The spec's only job is to describe what we're building, not how to behave.&lt;/p&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;Purpose&lt;/th&gt;
&lt;th&gt;Lifespan&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;How to work (style, linting, test commands)&lt;/td&gt;
&lt;td&gt;Permanent / project-wide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SPEC.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What to build (requirements, tasks, decisions)&lt;/td&gt;
&lt;td&gt;Ephemeral / feature-specific&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Mark Tasks Done, Update the Spec
&lt;/h3&gt;

&lt;p&gt;As each task finishes, I mark it &lt;code&gt;[x]&lt;/code&gt;:&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="p"&gt;-&lt;/span&gt; [x] Add &lt;span class="sb"&gt;`useTheme`&lt;/span&gt; hook in &lt;span class="sb"&gt;`src/hooks/useTheme.ts`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; [x] Update &lt;span class="sb"&gt;`ThemeProvider`&lt;/span&gt; to read from localStorage on mount
&lt;span class="p"&gt;-&lt;/span&gt; [ ] Add toggle button to &lt;span class="sb"&gt;`Header`&lt;/span&gt; component
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the approach changes mid-implementation, I update &lt;code&gt;## Decisions&lt;/code&gt; before continuing. The spec reflects reality, not the original plan.&lt;/p&gt;

&lt;p&gt;If a task turns out heavier than expected, I stop and break it into sub-tasks directly in the spec before continuing:&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="p"&gt;-&lt;/span&gt; [ ] Add toggle button to &lt;span class="sb"&gt;`Header`&lt;/span&gt; component
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Create &lt;span class="sb"&gt;`ThemeToggle`&lt;/span&gt; component
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Wire toggle to &lt;span class="sb"&gt;`useTheme`&lt;/span&gt; hook
&lt;span class="p"&gt;  -&lt;/span&gt; [ ] Add accessible label and keyboard support
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then &lt;code&gt;/clear&lt;/code&gt; and resume from the first sub-task. Same pattern, smaller scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;code&gt;/clear&lt;/code&gt; and Resume
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Resuming a session:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SPEC.md Continue from the next unchecked task.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One sentence. Claude reads the file, sees what's done, and picks up exactly where things left off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pausing mid-session:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;We are pausing here. Update SPEC.md with our current progress, mark completed tasks,
and add a note under a "## Next Steps" section for the next session. Then I will /clear.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This keeps the spec accurate even when a session ends mid-task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to &lt;code&gt;/clear&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;After writing the SPEC, before starting implementation&lt;/li&gt;
&lt;li&gt;After completing a task and marking it done&lt;/li&gt;
&lt;li&gt;When the agent starts contradicting prior decisions&lt;/li&gt;
&lt;li&gt;Between independent features&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't clear mid-task. Finish it, mark it done, then clear.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents Along the Way
&lt;/h2&gt;

&lt;p&gt;The SPEC.md is the backbone, but it's not the whole picture. Throughout the workflow I also trigger specialized agents at key moments. For example, a &lt;a href="https://github.com/helderberto/dotfiles/blob/main/dot_claude/agents/code-reviewer.md" rel="noopener noreferrer"&gt;&lt;code&gt;code-reviewer&lt;/code&gt;&lt;/a&gt; agent at the end of implementation to catch issues before committing.&lt;/p&gt;

&lt;p&gt;Claude Code supports custom agents and there are several I rely on regularly. That's a topic for a future post.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea
&lt;/h2&gt;

&lt;p&gt;The spec is the memory. The session is the execution.&lt;/p&gt;

&lt;p&gt;Every session starts by reading the spec. Every meaningful decision gets written back to it. When the session ends, nothing is lost. The spec has everything the next session needs to continue.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/clear&lt;/code&gt; isn't losing context. It's releasing noise and starting clean against a reliable source of truth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;But the spec isn't just for AI.&lt;/strong&gt; It's also:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding docs&lt;/strong&gt; for teammates picking up the feature mid-flight&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A PR description template&lt;/strong&gt; that explains what changed and why&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A reference when debugging&lt;/strong&gt; months later ("why did we choose cookies over localStorage?")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The spec starts as an AI tool. It ends as an engineering artifact.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;This workflow won't stay the same forever. AI tooling is moving fast and what works today will likely look primitive in a year. But the core habit of writing down what you're building before you build it is older than AI and will outlast it.&lt;/p&gt;

&lt;p&gt;I came across Martin Fowler's writing on &lt;a href="https://martinfowler.com/articles/exploring-gen-ai/sdd-3-tools.html" rel="noopener noreferrer"&gt;Spec-Driven Development&lt;/a&gt; after settling into this workflow. He covers tools like spec-kit that formalize parts of this process. I haven't tried them yet, but the underlying idea is the same: keep a living spec as the anchor between AI sessions.&lt;/p&gt;

&lt;p&gt;The spec is just a file. The discipline is what makes it work.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/ai-writes-code-you-own-quality-19g0"&gt;AI Writes Code. You Own Quality.&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/teaching-claude-code-your-standards-k9p"&gt;Teaching Claude Code Your Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/claude-code-hooks-1k7a"&gt;Claude Code Hooks&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>software</category>
      <category>development</category>
      <category>programming</category>
    </item>
    <item>
      <title>AI Writes Code. You Own Quality.</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Tue, 24 Mar 2026 15:43:40 +0000</pubDate>
      <link>https://forem.com/helderberto/ai-writes-code-you-own-quality-19g0</link>
      <guid>https://forem.com/helderberto/ai-writes-code-you-own-quality-19g0</guid>
      <description>&lt;p&gt;The more I use AI tools like Claude Code, the clearer it becomes: engineering skills are what make AI output worth shipping.&lt;/p&gt;

&lt;p&gt;AI makes writing code faster. But shipping good software still requires the same judgment it always did. Speed without engineering discipline just means shipping bugs faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  You Own the Code
&lt;/h2&gt;

&lt;p&gt;AI is a tool in your toolset. Like a compiler, a linter, or a test runner. It doesn't own the code. You do.&lt;/p&gt;

&lt;p&gt;When something breaks in production, nobody asks "which AI generated this?" They ask who shipped it. The PR has your name on it. The review was your responsibility. The decision to merge was yours.&lt;/p&gt;

&lt;p&gt;AI is a multiplier. If your engineering skills are weak, it multiplies that too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What AI Can't Do For You
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Think about edge cases.&lt;/strong&gt; AI covers the happy path. You guide it to the edges.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Understand the system.&lt;/strong&gt; AI sees the file. You see the architecture.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Make tradeoffs.&lt;/strong&gt; AI doesn't know your team's priorities, deadlines, or tech debt tolerance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Carry team context.&lt;/strong&gt; You were in the meeting where the team decided to deprecate that service. You know the naming conventions, the architectural decisions, the "we tried X and it didn't work" history. AI has none of that unless you provide it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Guide AI With Tests
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Red-Green-Refactor
&lt;/h3&gt;

&lt;p&gt;TDD becomes even more powerful with AI. The engineer defines WHAT to test. AI handles the HOW.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Red.&lt;/strong&gt; Write failing tests that cover expected behavior and edge cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SearchFilter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renders input with placeholder&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchFilter&lt;/span&gt; &lt;span class="na"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByPlaceholderText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Search products...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;calls onSearch after user stops typing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchFilter&lt;/span&gt; &lt;span class="na"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;debounceMs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;searchbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shoes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shoes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;does not call onSearch for empty input&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchFilter&lt;/span&gt; &lt;span class="na"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;debounceMs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;searchbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;searchbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;not&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shows loading spinner while searching&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchFilter&lt;/span&gt; &lt;span class="na"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trims whitespace before calling onSearch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onSearch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SearchFilter&lt;/span&gt; &lt;span class="na"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;debounceMs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;searchbox&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;  shoes  &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onSearch&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shoes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You wrote zero implementation. But you defined the component's contract, its edge cases, and its behavior. That's engineering.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Green.&lt;/strong&gt; AI implements the minimal code to pass all tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Refactor.&lt;/strong&gt; You guide AI to clean up. Extract helpers, apply single responsibility, name things clearly. The goal: make it easy for the next engineer who touches this code.&lt;/p&gt;

&lt;p&gt;Without test discipline, AI gives you untested code that "looks right." With TDD, AI works within constraints you defined.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cover Entire Flows With E2E Tests
&lt;/h3&gt;

&lt;p&gt;Unit tests verify pieces. E2E tests verify the whole flow works together.&lt;/p&gt;

&lt;p&gt;AI can scaffold e2e tests, but you define which flows are critical. A checkout flow, an authentication sequence, a data export pipeline. These are decisions that require understanding the business, not just the code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;user completes checkout flow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="add-to-cart"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="checkout"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test@example.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#card-number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4242424242424242&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-testid="place-order"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;locator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.confirmation&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeVisible&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You defined the critical path. AI can fill in the details, add assertions, handle setup/teardown. But the decision of WHAT to test end-to-end is yours. The same applies to edge cases: what happens when payment fails? When the session expires mid-checkout? When the cart is empty? You define those scenarios. AI writes the assertions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforce Standards Before Code Ships
&lt;/h2&gt;

&lt;p&gt;Standards only matter if they're enforced. Three layers:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linting rules.&lt;/strong&gt; Create rules that encode team conventions. AI follows them when configured, but you need to know which rules matter for your codebase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git hooks.&lt;/strong&gt; Pre-push hooks that run linting and tests. Code that doesn't pass doesn't ship. No exceptions, not even for AI-generated code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI tool hooks.&lt;/strong&gt; Tools like Claude Code support &lt;a href="https://dev.to/helderberto/claude-code-hooks-1k7a"&gt;hooks&lt;/a&gt; that intercept actions and enforce standards automatically. Run lint before every commit. Run tests before every push. The AI operates within guardrails you defined.&lt;/p&gt;

&lt;p&gt;The engineer's job: define the guardrails. AI works within them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Close the Loop With Verification Tools
&lt;/h2&gt;

&lt;p&gt;Every AI-generated change needs verification. The faster you catch problems, the faster AI can fix them.&lt;/p&gt;

&lt;p&gt;Verification feedback loops exist at every level: CI pipelines that run visual regression tests, browser automation that captures screenshots, performance audits that flag regressions. The principle is the same. Every output becomes input for the next iteration.&lt;/p&gt;

&lt;p&gt;In my workflow, I use Playwright MCP and Chrome DevTools MCP to close this loop directly inside the AI session:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screenshot shows a broken layout? AI fixes the CSS.&lt;/li&gt;
&lt;li&gt;Console error from a missing prop? AI adds the prop.&lt;/li&gt;
&lt;li&gt;Lighthouse audit flags an accessibility issue? AI adds the missing aria label.&lt;/li&gt;
&lt;li&gt;Network tab shows a redundant API call? AI refactors the data fetching.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This turns AI from "generate and hope" into "generate, verify, iterate." The engineer who sets up this loop gets better results than one who just prompts and ships.&lt;/p&gt;

&lt;p&gt;The skill isn't writing the code. It's knowing what to verify and how to feed that information back.&lt;/p&gt;

&lt;h2&gt;
  
  
  Review AI Output Like You'd Review a Junior's PR
&lt;/h2&gt;

&lt;p&gt;AI-generated code compiles. It passes the tests you wrote. It looks reasonable. But that doesn't mean it's good.&lt;/p&gt;

&lt;p&gt;Read the diff. Every line. Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unnecessary complexity.&lt;/strong&gt; AI loves abstractions. Does this need a factory pattern, or would a plain function do?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subtle bugs.&lt;/strong&gt; Off-by-one errors, missing null checks, race conditions. AI generates plausible code, not provably correct code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deviations from patterns.&lt;/strong&gt; Your codebase uses a specific error handling pattern. AI might invent a different one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security holes.&lt;/strong&gt; Unsanitized input, exposed secrets, missing auth checks. AI doesn't think adversarially by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The skill of reading code critically matters more when someone (or something) else writes it. You can't review what you don't understand. Invest in reading code as much as writing it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment the Why, Not the How
&lt;/h2&gt;

&lt;p&gt;AI tends to over-comment. It explains the obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Loop through the array and filter items&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Set the state with filtered items&lt;/span&gt;
&lt;span class="nf"&gt;setItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These comments add noise. The code already says HOW it works.&lt;/p&gt;

&lt;p&gt;Good comments explain why something exists or what business logic it represents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Archived items are processed by a nightly batch job, not shown in the UI&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;active&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But before writing any comment, ask: can the code explain itself? A well-named function or variable often eliminates the need for a comment entirely. Comments should exist only when the code can't tell the full story on its own.&lt;/p&gt;

&lt;p&gt;That's the difference between AI-generated noise and engineering judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Small Chunks for the Next Engineer
&lt;/h2&gt;

&lt;p&gt;AI generates 500 lines in seconds. Your job: break it into reviewable, understandable pieces.&lt;/p&gt;

&lt;p&gt;Extract functions. Apply single responsibility. Name things clearly. The next engineer (or the next AI session) touching this code benefits from clean structure.&lt;/p&gt;

&lt;p&gt;This is a human judgment call. AI optimizes for the current prompt. You optimize for the project's lifetime. A function that does one thing well is easier to test, easier to reuse, and easier to replace than a monolith that "works."&lt;/p&gt;

&lt;p&gt;Small PRs &amp;gt; big PRs, even when AI writes them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;You own the code.&lt;/strong&gt; AI is a tool, not an excuse. Your name is on the PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TDD guides AI.&lt;/strong&gt; Write failing tests first, let AI implement, then refactor.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E tests catch what unit tests miss.&lt;/strong&gt; Define the critical flows and edge cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce standards with linting and hooks.&lt;/strong&gt; Guardrails before code ships.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close the loop.&lt;/strong&gt; Feed screenshots, errors, and audits back to AI for better iterations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review like it's a junior's PR.&lt;/strong&gt; Read every line. Question every abstraction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comment the why, not the how.&lt;/strong&gt; Business context over implementation details.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small chunks &amp;gt; big dumps.&lt;/strong&gt; Break AI output into pieces the next engineer can follow.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;AI didn't make engineering skills optional. It made them the differentiator.&lt;/p&gt;

&lt;p&gt;AI is a tool. A powerful one. But tools don't ship software, engineers do. Study the fundamentals. Master testing. Understand your architecture. Define your standards. Then use AI to execute at a speed you never could alone.&lt;/p&gt;

&lt;p&gt;The code is AI's job. The quality is yours.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>software</category>
      <category>development</category>
      <category>programming</category>
    </item>
    <item>
      <title>Claude Code Hooks</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Thu, 19 Mar 2026 17:05:16 +0000</pubDate>
      <link>https://forem.com/helderberto/claude-code-hooks-1k7a</link>
      <guid>https://forem.com/helderberto/claude-code-hooks-1k7a</guid>
      <description>&lt;p&gt;Claude Code moves fast: editing files, running commands, pushing code in quick succession. Hooks let you intercept that flow and enforce your standards automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Hooks?
&lt;/h2&gt;

&lt;p&gt;Hooks are shell commands that run at lifecycle events during a Claude Code session. They're configured in &lt;code&gt;settings.json&lt;/code&gt; and execute outside Claude's control. The harness runs them, not the AI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This matters:&lt;/strong&gt; Claude can't skip hooks or work around them. They're infrastructure, not suggestions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hook Types
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Hook&lt;/th&gt;
&lt;th&gt;When it runs&lt;/th&gt;
&lt;th&gt;Can block?&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 a tool executes&lt;/td&gt;
&lt;td&gt;Yes (non-zero exit)&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 a tool executes&lt;/td&gt;
&lt;td&gt;No&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 Claude sends a notification&lt;/td&gt;
&lt;td&gt;No&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 Claude's turn ends&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SubagentStop&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When a subagent finishes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;PreToolUse&lt;/code&gt; is the most powerful: it can stop a tool from running entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;Hooks live in &lt;code&gt;settings.json&lt;/code&gt;. Two scopes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.claude/settings.json&lt;/code&gt; (global, applies everywhere)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.claude/settings.json&lt;/code&gt; (project-level, merged with global)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Structure:&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;"Bash"&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;"your-shell-command-here"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Running check..."&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;Fields:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;matcher&lt;/code&gt;: tool name to match (&lt;code&gt;"Bash"&lt;/code&gt;, &lt;code&gt;"Edit"&lt;/code&gt;, &lt;code&gt;"Write"&lt;/code&gt;, &lt;code&gt;""&lt;/code&gt; for all)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;command&lt;/code&gt;: shell command to run&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;timeout&lt;/code&gt;: milliseconds before the hook is killed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;statusMessage&lt;/code&gt;: shown in the UI while the hook runs&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Hook Input
&lt;/h2&gt;

&lt;p&gt;Hooks receive the tool's input via &lt;code&gt;stdin&lt;/code&gt; as JSON. Use &lt;code&gt;jq&lt;/code&gt; to extract what you need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Get the bash command being run&lt;/span&gt;
&lt;span class="nv"&gt;cmd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.command'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Get the file path being edited&lt;/span&gt;
&lt;span class="nv"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.tool_input.file_path'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets you write conditional hooks that only fire for specific operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practical Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Lint Before Push
&lt;/h3&gt;

&lt;p&gt;Block &lt;code&gt;git push&lt;/code&gt; unless lint passes:&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;"Bash"&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;"cmd=$(jq -r '.tool_input.command'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$cmd&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -q 'git push' || exit 0; npm run lint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Running lint before push..."&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;code&gt;exit 0&lt;/code&gt; on non-push commands lets them through. Only push triggers the lint check. Lint fails = push blocked.&lt;/p&gt;

&lt;h3&gt;
  
  
  Type-Check After TypeScript Edits
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;tsc&lt;/code&gt; after editing &lt;code&gt;.ts&lt;/code&gt; files:&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"&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;"path=$(jq -r '.tool_input.file_path'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -q '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.ts$' || exit 0; npx tsc --noEmit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Type-checking..."&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;h3&gt;
  
  
  Desktop Notification When Claude Stops
&lt;/h3&gt;

&lt;p&gt;Know when a long task finishes without watching the terminal:&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;"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;""&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;"osascript -e 'display notification &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude finished&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; with title &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Claude Code&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="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;h3&gt;
  
  
  Block Writes to Sensitive Files
&lt;/h3&gt;

&lt;p&gt;Prevent accidental edits to &lt;code&gt;.env&lt;/code&gt; files:&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"&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;"path=$(jq -r '.tool_input.file_path'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -q '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.env' &amp;amp;&amp;amp; echo 'Blocked: cannot write to .env files' &amp;amp;&amp;amp; exit 1 || 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;Non-zero exit blocks the tool. Claude sees the output and adjusts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend-Specific Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Run Tests After Component Changes
&lt;/h3&gt;

&lt;p&gt;Trigger Vitest after editing React components:&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"&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;"path=$(jq -r '.tool_input.file_path'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -qE '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.(tsx|jsx)$' || exit 0; npx vitest run --reporter=verbose 2&amp;gt;&amp;amp;1 | tail -20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Running component tests..."&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;Only fires on &lt;code&gt;.tsx&lt;/code&gt;/&lt;code&gt;.jsx&lt;/code&gt; edits. You see failing tests immediately, not after the next push.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block &lt;code&gt;console.log&lt;/code&gt; Before Commit
&lt;/h3&gt;

&lt;p&gt;Prevent debug statements from shipping:&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;"Bash"&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;"cmd=$(jq -r '.tool_input.command'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$cmd&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -q 'git commit' || exit 0; git diff --cached | grep -q 'console&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.log' &amp;amp;&amp;amp; echo 'Blocked: staged changes contain console.log' &amp;amp;&amp;amp; exit 1 || exit 0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Checking for console.log..."&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;Scans the staged diff before every commit. Fails if &lt;code&gt;console.log&lt;/code&gt; is found.&lt;/p&gt;

&lt;h3&gt;
  
  
  ESLint on Every File Write
&lt;/h3&gt;

&lt;p&gt;Catch lint errors the moment a file is created:&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;"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;"path=$(jq -r '.tool_input.file_path'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -qE '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.(ts|tsx|js|jsx)$' || exit 0; npx eslint &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; --max-warnings=0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Linting new file..."&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;Useful when Claude writes new files from scratch. Errors surface before any follow-up edits build on broken code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility Check After Component Edits
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;axe-core&lt;/code&gt; via CLI after touching UI components:&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"&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;"path=$(jq -r '.tool_input.file_path'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -q 'components/' || exit 0; echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -qE '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.tsx$' || exit 0; npx axe-cli http://localhost:3000 --exit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Checking accessibility..."&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;Requires a running dev server. Best paired with a &lt;code&gt;PreToolUse&lt;/code&gt; hook that starts it if not running.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prettier Format Check After Write
&lt;/h3&gt;

&lt;p&gt;Enforce consistent formatting on new files without running format-on-save globally:&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;"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;"path=$(jq -r '.tool_input.file_path'); echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt; | grep -qE '&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;.(ts|tsx|css|json)$' || exit 0; npx prettier --check &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&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;Run: npx prettier --write &lt;/span&gt;&lt;span class="se"&gt;\\\"&lt;/span&gt;&lt;span class="s2"&gt;$path&lt;/span&gt;&lt;span class="se"&gt;\\\"\"&lt;/span&gt;&lt;span class="s2"&gt; &amp;amp;&amp;amp; exit 1)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"timeout"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"statusMessage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Checking formatting..."&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;Outputs the fix command when it fails, so Claude can auto-correct.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hooks vs. CLAUDE.md
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CLAUDE.md&lt;/strong&gt; tells Claude what to do. Claude might forget or skip steps under pressure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hooks&lt;/strong&gt; run regardless of what Claude does. They're external to the AI.&lt;/p&gt;

&lt;p&gt;Use CLAUDE.md for preferences. Use hooks for guarantees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; "Run lint before committing" in CLAUDE.md = a suggestion. A PreToolUse hook on Bash that checks for &lt;code&gt;git commit&lt;/code&gt; = enforced.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ordering Multiple Hooks
&lt;/h2&gt;

&lt;p&gt;You can stack hooks on the same event:&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;"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;"Bash"&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;"check-push-lint.sh"&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;"check-push-secrets.sh"&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;Hooks run in order. First failure blocks the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Use Hooks For
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Lint before push (catches style errors before CI)&lt;/li&gt;
&lt;li&gt;Type-check on edit (immediate feedback on TypeScript errors)&lt;/li&gt;
&lt;li&gt;Tests after component changes (know immediately if something broke)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console.log&lt;/code&gt; guard on commit (never ship debug statements)&lt;/li&gt;
&lt;li&gt;Secret scanning on push (last line of defense before remote)&lt;/li&gt;
&lt;li&gt;Notifications on stop (no more watching the terminal)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When Not to Use Hooks
&lt;/h2&gt;

&lt;p&gt;The real cost isn't latency. A &lt;code&gt;grep&lt;/code&gt; or &lt;code&gt;jq&lt;/code&gt; command adds single-digit milliseconds. The cost is pairing a &lt;strong&gt;slow operation with a high-frequency tool call&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pairing heavy operations with broad matchers. Running &lt;code&gt;tsc --noEmit&lt;/code&gt; on every &lt;code&gt;Edit&lt;/code&gt; recompiles the full project each time. Scope it to &lt;code&gt;Bash&lt;/code&gt; and trigger only on &lt;code&gt;git push&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Overly broad matchers. If your hook only cares about &lt;code&gt;.ts&lt;/code&gt; files, don't match all &lt;code&gt;Write&lt;/code&gt; calls and filter inside the script. Use the most specific matcher you can.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lightweight checks (grep, jq, file path tests) are essentially free. Save the heavy tools (tsc, vitest, npm run build) for infrequent triggers like &lt;code&gt;git push&lt;/code&gt; or &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Hooks are the difference between hoping Claude does the right thing and knowing it will.&lt;/p&gt;

&lt;p&gt;Your CI catches broken builds. Your hooks catch them before the push. The earlier you catch problems, the cheaper they are to fix.&lt;/p&gt;

&lt;p&gt;Define your quality gates once. Let hooks enforce them on every session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/teaching-claude-code-your-standards-k9p"&gt;Teaching Claude Code Your Standards&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/building-shareable-ai-agent-skills-d9l"&gt;Building Shareable AI Agent Skills&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/helderberto/what-your-claude-code-agents-dont-need-to-be-told-4ed5"&gt;What Your Claude Code Agents Don't Need to Be Told&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://code.claude.com/docs/en/hooks" rel="noopener noreferrer"&gt;Claude Code Hooks documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>claude</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Building Shareable AI Agent Skills</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Fri, 13 Feb 2026 11:36:54 +0000</pubDate>
      <link>https://forem.com/helderberto/building-shareable-ai-agent-skills-d9l</link>
      <guid>https://forem.com/helderberto/building-shareable-ai-agent-skills-d9l</guid>
      <description>&lt;p&gt;You're typing the same instructions to Claude Code every session. "Create a commit following repo style." "Run tests before pushing." "Check coverage on these changes."&lt;/p&gt;

&lt;p&gt;What if those workflows were reusable slash commands? What if they worked in Cursor, Continue, and Cline too?&lt;/p&gt;

&lt;p&gt;That's what Skills are - version-controlled AI workflows that work across agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;AI agents lose context between sessions. Every new chat, you're re-explaining:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Git workflow preferences&lt;/li&gt;
&lt;li&gt;PR creation templates&lt;/li&gt;
&lt;li&gt;Testing requirements&lt;/li&gt;
&lt;li&gt;Quality gates before shipping&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You're doing the AI's job - providing context - instead of letting it do yours.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Skills as Code
&lt;/h2&gt;

&lt;p&gt;Skills are markdown files that encode workflows. Install once, invoke with &lt;code&gt;/skill-name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/ship&lt;/code&gt; = stage, commit with style-matched message, push, verify.&lt;/p&gt;

&lt;p&gt;No more manual &lt;code&gt;git add -A &amp;amp;&amp;amp; git commit &amp;amp;&amp;amp; git push&lt;/code&gt; dance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Skills Matter
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cross-agent portability&lt;/strong&gt; - Write once, works in multiple agents:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Claude Code&lt;/li&gt;
&lt;li&gt;Cursor&lt;/li&gt;
&lt;li&gt;Continue&lt;/li&gt;
&lt;li&gt;Cline&lt;/li&gt;
&lt;li&gt;Windsurf&lt;/li&gt;
&lt;li&gt;Replit&lt;/li&gt;
&lt;li&gt;+ more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Version controlled&lt;/strong&gt; - Skills are git-tracked markdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterate based on real usage&lt;/li&gt;
&lt;li&gt;Roll back if needed&lt;/li&gt;
&lt;li&gt;See what changed and why&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Shareable&lt;/strong&gt; - Install anyone's skills:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add helderberto/skills
npx skills add vercel-labs/agent-skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Team workflows&lt;/strong&gt; - Codify team conventions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commit message style&lt;/li&gt;
&lt;li&gt;PR templates&lt;/li&gt;
&lt;li&gt;Security checks&lt;/li&gt;
&lt;li&gt;Quality gates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Context efficiency&lt;/strong&gt; - Skills reduce token usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/ship&lt;/code&gt; uses fewer tokens than repeating full workflow instructions&lt;/li&gt;
&lt;li&gt;Agent loads skill once, executes steps without re-explanation&lt;/li&gt;
&lt;li&gt;Longer conversations stay within context window limits&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Example: Evolving the Ship Skill
&lt;/h2&gt;

&lt;p&gt;Started simple:&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="p"&gt;1.&lt;/span&gt; Stage all changes
&lt;span class="p"&gt;2.&lt;/span&gt; Commit
&lt;span class="p"&gt;3.&lt;/span&gt; Push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hit a problem: Failed on non-Node projects when trying to run &lt;code&gt;npm test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt;&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="p"&gt;3.&lt;/span&gt; Check for quality checks:
&lt;span class="p"&gt;   -&lt;/span&gt; If package.json exists, check for lint and test scripts
&lt;span class="p"&gt;   -&lt;/span&gt; Run available checks in parallel
&lt;span class="p"&gt;   -&lt;/span&gt; If no package.json, skip quality checks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pushed update. Everyone benefits via &lt;code&gt;npx skills update&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is what version control for AI instructions looks like.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Your Skills Repo
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Create repository structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;skills/
├── ship/
│   └── SKILL.md
├── commit/
│   └── SKILL.md
└── coverage/
    └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Write SKILL.md:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;&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="nn"&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;ship&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Stage&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changes,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;push"&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Ship Changes&lt;/span&gt;

&lt;span class="gu"&gt;## Workflow&lt;/span&gt;
&lt;span class="p"&gt;
1.&lt;/span&gt; Run git status (never -uall)
&lt;span class="p"&gt;2.&lt;/span&gt; Review all changes
&lt;span class="p"&gt;3.&lt;/span&gt; Generate commit message matching repo style
&lt;span class="p"&gt;4.&lt;/span&gt; Stage all files: git add -A
&lt;span class="p"&gt;5.&lt;/span&gt; Commit with HEREDOC format
&lt;span class="p"&gt;6.&lt;/span&gt; Push to current branch
&lt;span class="p"&gt;7.&lt;/span&gt; Verify with git status

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Match repo commit style from git log
&lt;span class="p"&gt;-&lt;/span&gt; NEVER force push
&lt;span class="p"&gt;-&lt;/span&gt; NEVER skip hooks
&lt;span class="p"&gt;-&lt;/span&gt; Push to current branch only
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Install:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add yourusername/skills &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Use:&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# In Claude Code, Cursor, Continue, etc.&lt;/span&gt;
/ship
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  My Skills Collection
&lt;/h2&gt;

&lt;p&gt;I maintain a collection covering git workflows, quality gates, safety checks, and development practices at &lt;a href="https://github.com/helderberto/skills" rel="noopener noreferrer"&gt;helderberto/skills&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Install: &lt;code&gt;npx skills add helderberto/skills --all&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Skills vs. Docs
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Docs&lt;/strong&gt; (CLAUDE.md) = passive instructions AI reads&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills&lt;/strong&gt; = active workflows AI executes&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key difference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLAUDE.md loads &lt;strong&gt;every session&lt;/strong&gt; - takes context tokens upfront&lt;/li&gt;
&lt;li&gt;Skills load &lt;strong&gt;only when invoked&lt;/strong&gt; - no token cost until you use them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;When to use each:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CLAUDE.md: Always-on rules (code style, immutability, testing principles)&lt;/li&gt;
&lt;li&gt;Skills: On-demand workflows (commit, PR creation, coverage checks)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Docs say "prefer immutability." Skills enforce it by running lint checks before commit.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Put "no array mutations" in CLAUDE.md. Put "/ship workflow with lint checks" in skills.&lt;/p&gt;

&lt;p&gt;Both matter. Docs set standards. Skills enforce them. Skills save context for when you need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Skills Work
&lt;/h2&gt;

&lt;p&gt;The Skills CLI uses symlinks to share files across agents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.agents/skills/ship/     &lt;span class="c"&gt;# Source&lt;/span&gt;
~/.claude/skills/ship/     &lt;span class="c"&gt;# Symlink for Claude Code&lt;/span&gt;
~/.cursor/skills/ship/     &lt;span class="c"&gt;# Symlink for Cursor&lt;/span&gt;
~/.continue/skills/ship/   &lt;span class="c"&gt;# Symlink for Continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file, multiple agent directories.&lt;/p&gt;

&lt;p&gt;Update the source, all agents see the changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discovery and Sharing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Find skills:&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills find typescript
npx skills find testing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install specific skills:&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add helderberto/skills &lt;span class="nt"&gt;--skill&lt;/span&gt; ship &lt;span class="nt"&gt;--skill&lt;/span&gt; commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Install everything:&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills add helderberto/skills &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update all:&lt;/strong&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx skills update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Browse more: &lt;a href="https://skills.sh" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Benefits in Practice
&lt;/h2&gt;

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

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

&lt;ul&gt;
&lt;li&gt;Repeat instructions every session&lt;/li&gt;
&lt;li&gt;Inconsistent commit messages&lt;/li&gt;
&lt;li&gt;Forget quality checks&lt;/li&gt;
&lt;li&gt;Manual git commands&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/ship&lt;/code&gt; handles entire workflow&lt;/li&gt;
&lt;li&gt;Commits match repo style automatically&lt;/li&gt;
&lt;li&gt;Quality gates always run&lt;/li&gt;
&lt;li&gt;Zero manual git operations&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Skills replace dozens of repeated prompts&lt;/li&gt;
&lt;li&gt;Works across all my coding agents&lt;/li&gt;
&lt;li&gt;Team members install same skills = consistent workflows&lt;/li&gt;
&lt;li&gt;Improvements benefit everyone via update&lt;/li&gt;
&lt;li&gt;Fewer tokens spent on context = longer, more productive conversations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Skills = Infrastructure for AI
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This isn't about shortcuts. It's about encoding:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Team conventions (commit message style, PR templates, etc.)&lt;/li&gt;
&lt;li&gt;Security practices&lt;/li&gt;
&lt;li&gt;Quality gates (lint, test, coverage)&lt;/li&gt;
&lt;li&gt;Organizational knowledge&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create skills repo&lt;/li&gt;
&lt;li&gt;Write 2-3 workflows you repeat most&lt;/li&gt;
&lt;li&gt;Install with &lt;code&gt;npx skills add yourusername/skills&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Iterate based on real usage&lt;/li&gt;
&lt;li&gt;Share with team/community&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Start small. My first skill was just "commit and push." It evolved from there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;You're already teaching AI agents your workflows. Skills make those teachings reusable, shareable, and version controlled.&lt;/p&gt;

&lt;p&gt;Stop repeating yourself. Start building skills.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;My skills: &lt;a href="https://github.com/helderberto/skills" rel="noopener noreferrer"&gt;github.com/helderberto/skills&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Skills CLI: &lt;a href="https://skills.sh" rel="noopener noreferrer"&gt;skills.sh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vercel's collection: &lt;a href="https://github.com/vercel-labs/agent-skills" rel="noopener noreferrer"&gt;github.com/vercel-labs/agent-skills&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Write once. Use everywhere. Improve continuously.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>skills</category>
      <category>programming</category>
    </item>
    <item>
      <title>What Your Claude Code Agents Don't Need to Be Told</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Mon, 09 Feb 2026 17:44:21 +0000</pubDate>
      <link>https://forem.com/helderberto/what-your-claude-code-agents-dont-need-to-be-told-4ed5</link>
      <guid>https://forem.com/helderberto/what-your-claude-code-agents-dont-need-to-be-told-4ed5</guid>
      <description>&lt;p&gt;Claude Code has a finite context window. Every token matters. Your agent definitions, skill files, and docs all compete for that space with the actual code you're asking it to analyze.&lt;/p&gt;

&lt;p&gt;The problem: it's tempting to write exhaustive agent configurations, detailed examples, scripted responses, repeated boilerplate. It feels like you're being thorough. But you're actually teaching the model things it already knows, while leaving less room for the things it doesn't.&lt;/p&gt;

&lt;p&gt;I hit this wall while working on a frontend feature. My agents were loaded with generic programming examples, and I started wondering: is any of this actually helping, or is it just consuming context?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is a follow-up to &lt;a href="https://dev.to/helderberto/teaching-claude-code-your-standards-k9p"&gt;Teaching Claude Code Your Standards&lt;/a&gt;, where I covered the initial setup of &lt;code&gt;~/.claude/&lt;/code&gt; with docs, skills, and agents.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Real Problem: Teaching the Model What It Already Knows
&lt;/h2&gt;

&lt;p&gt;My TypeScript enforcer agent included examples of how to use spread operators, how to write early returns, and when to use &lt;code&gt;Set&lt;/code&gt; instead of &lt;code&gt;Array.find()&lt;/code&gt;. The model already knows all of that.&lt;/p&gt;

&lt;p&gt;What it &lt;em&gt;doesn't&lt;/em&gt; know: our project uses a formatjs babel plugin with &lt;code&gt;ast: true&lt;/code&gt;, which compiles i18n messages to AST at build time. This causes parameterized messages like &lt;code&gt;{count} active&lt;/code&gt; to render variable names literally in tests. That's a gotcha worth documenting, it cost me real debugging time.&lt;/p&gt;

&lt;p&gt;The distinction is simple: &lt;strong&gt;generic programming knowledge is noise. Project-specific knowledge is signal.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Filters for Every Line in Your Config
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Does the model already know this?
&lt;/h3&gt;

&lt;p&gt;The model knows common patterns. It knows early returns, immutability with spread, N+1 query problems, React memoization. Including examples of these doesn't make it better at applying them, it just takes up space.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete&lt;/strong&gt;: Generic code examples, common design patterns, standard best practices.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Keep&lt;/strong&gt;: Gotchas specific to your project, team conventions the model can't infer, configuration quirks that cause surprising behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Is this repeated across agents?
&lt;/h3&gt;

&lt;p&gt;I found identical sections copy-pasted across every agent:&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;## Commands to Use&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Glob`&lt;/span&gt; - Find files
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Grep`&lt;/span&gt; - Search for patterns
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Read`&lt;/span&gt; - Examine content
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`Bash`&lt;/span&gt; - Run scripts

&lt;span class="gu"&gt;## Success Criteria&lt;/span&gt;
The agent is successful when...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model already knows its available tools from the system prompt. "Success criteria" sections just reword the agent's description. These are filler.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Is this a checklist or an essay?
&lt;/h3&gt;

&lt;p&gt;The model doesn't need you to explain &lt;em&gt;how&lt;/em&gt; to extract a function. It needs to know &lt;em&gt;when&lt;/em&gt; you want it flagged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before (essay with examples):&lt;/strong&gt;&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;### Readability Review&lt;/span&gt;

Evaluate function complexity and cognitive load...

&lt;span class="gs"&gt;**Long functions example:**&lt;/span&gt;
// ⚠️ Too long (&amp;gt;50 lines)
const processOrder = (order) =&amp;gt; {
  // ... 80 lines of logic
};

// ✅ Extracted
const validateOrder = (order) =&amp;gt; { /&lt;span class="ge"&gt;* ... *&lt;/span&gt;/ };
const calculateTotal = (order) =&amp;gt; { /&lt;span class="ge"&gt;* ... *&lt;/span&gt;/ };
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After (checklist):&lt;/strong&gt;&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;### Readability&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Functions &amp;gt;50 lines → extract
&lt;span class="p"&gt;-&lt;/span&gt; Nesting &amp;gt;3 levels → early returns
&lt;span class="p"&gt;-&lt;/span&gt; Magic numbers → named constants
&lt;span class="p"&gt;-&lt;/span&gt; Unclear names
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same rules. The model fills in the &lt;em&gt;how&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents vs Skills: Different Problems, Different Tools
&lt;/h2&gt;

&lt;p&gt;I had a "dependency auditor" agent, hundreds of lines of instructions for what amounts to running &lt;code&gt;npm audit&lt;/code&gt; and &lt;code&gt;npm outdated&lt;/code&gt;. That's not a judgment call. That's a command sequence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agents&lt;/strong&gt; solve problems requiring judgment: code review, test quality assessment, TypeScript enforcement. They need checklists of &lt;em&gt;what to look for&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Skills&lt;/strong&gt; solve problems with deterministic steps: commit, push, lint, audit. They need a sequence of commands and rules about when to stop.&lt;/p&gt;

&lt;p&gt;The dependency auditor became a skill:&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;# Dependency Audit&lt;/span&gt;

&lt;span class="gu"&gt;## Commands&lt;/span&gt;
Run in parallel:
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm audit`&lt;/span&gt; - security vulnerabilities
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="sb"&gt;`npm outdated`&lt;/span&gt; - outdated packages

&lt;span class="gu"&gt;## Workflow&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Run security audit and outdated check
&lt;span class="p"&gt;2.&lt;/span&gt; Report critical vulnerabilities with fix commands
&lt;span class="p"&gt;3.&lt;/span&gt; List outdated packages (major vs minor/patch)
&lt;span class="p"&gt;4.&lt;/span&gt; Check for unused deps: grep imports in src/

&lt;span class="gu"&gt;## Rules&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use &lt;span class="sb"&gt;`npm audit`&lt;/span&gt;, never &lt;span class="sb"&gt;`npx`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Prioritize: security &amp;gt; major updates &amp;gt; unused &amp;gt; minor updates
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clear. Deterministic. No essays needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overlapping Agents Dilute Focus
&lt;/h2&gt;

&lt;p&gt;I had three agents doing related work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;code-review&lt;/strong&gt;: logic errors, readability, pattern consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;refactor-assistant&lt;/strong&gt;: code smells, complexity, dead code, duplication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;perf-analyzer&lt;/strong&gt;: algorithm complexity, unnecessary re-renders, memoization&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The overlap was significant. The refactor-assistant's unique value was a code smells checklist. The perf-analyzer's unique value was React-specific checks. Both fit naturally as sections in one focused code-review agent.&lt;/p&gt;

&lt;p&gt;One agent with clear sections beats three agents competing for the same territory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Your Workflows Should Fail Fast
&lt;/h2&gt;

&lt;p&gt;My &lt;code&gt;/ship&lt;/code&gt; skill (stage → commit → push) had no quality gates. It would happily push broken code. The fix was simple:&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;## Workflow&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Review all changes
&lt;span class="p"&gt;2.&lt;/span&gt; Run quality checks in parallel:
&lt;span class="p"&gt;   -&lt;/span&gt; &lt;span class="sb"&gt;`npm run lint`&lt;/span&gt;
&lt;span class="p"&gt;   -&lt;/span&gt; &lt;span class="sb"&gt;`npm test`&lt;/span&gt;
&lt;span class="p"&gt;3.&lt;/span&gt; If checks fail: report errors, STOP do not commit or push
&lt;span class="p"&gt;4.&lt;/span&gt; Generate commit message
&lt;span class="p"&gt;5.&lt;/span&gt; Stage, commit, push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key is the hard-stop rule. Without it, the skill optimizes for speed over correctness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Document What Only You Know
&lt;/h2&gt;

&lt;p&gt;The highest-value documentation is knowledge the model can't infer from your code alone:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internationalization gotchas&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whitespace between elements: use layout (Flex/gap) instead of &lt;code&gt;{' '}&lt;/code&gt; - linters flag literal strings in JSX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Accessibility testing&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getByRole&lt;/code&gt; as primary query - if it can't find the element, the a11y is likely wrong&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;getByRole&lt;/code&gt; over &lt;code&gt;container.querySelector&lt;/code&gt; - query by accessibility, not DOM structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Team conventions&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vi.spyOn&lt;/code&gt; over &lt;code&gt;vi.mock&lt;/code&gt; for your own code&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;vi.mock&lt;/code&gt; only for third-party libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are decisions your team made. The model can't infer them from reading your code. They prevent real mistakes. That's what your config should contain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Can't Prove
&lt;/h2&gt;

&lt;p&gt;I can't prove this made my agents "better." There's no benchmark for agent configuration effectiveness. What I can say: the content I removed was generic programming knowledge the model already has. The content I kept, and added, is specific to my projects and workflows. Every token saved on instructions is a token available for analyzing actual code.&lt;/p&gt;

&lt;p&gt;Whether that tradeoff matters depends on how close to the context limit you work. For long sessions with large codebases, it adds up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Generic knowledge is noise&lt;/strong&gt;: If the model already knows it, it doesn't belong in your config&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Checklists over essays&lt;/strong&gt;: Tell it &lt;em&gt;what&lt;/em&gt; to check, not &lt;em&gt;how&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills for commands, agents for judgment&lt;/strong&gt;: Match the tool to the problem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One focused agent beats three overlapping ones&lt;/strong&gt;: Merge and consolidate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Document what only you know&lt;/strong&gt;: Project gotchas, team conventions, configuration quirks&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflows should fail fast&lt;/strong&gt;: Add quality gates before destructive operations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Your Claude Code configuration should contain what only &lt;em&gt;you&lt;/em&gt; know about your project. Everything else is already in the model.&lt;/p&gt;

&lt;p&gt;My full config: &lt;a href="https://github.com/helderberto/dotfiles/tree/main/claude/.claude" rel="noopener noreferrer"&gt;github.com/helderberto/dotfiles&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>contextwindow</category>
    </item>
    <item>
      <title>Teaching Claude Code Your Standards</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Fri, 06 Feb 2026 11:28:22 +0000</pubDate>
      <link>https://forem.com/helderberto/teaching-claude-code-your-standards-k9p</link>
      <guid>https://forem.com/helderberto/teaching-claude-code-your-standards-k9p</guid>
      <description>&lt;p&gt;Ever had an AI refactor your entire file when you asked for a one-line fix? Or add comments to every function?&lt;/p&gt;

&lt;p&gt;Claude Code's power comes from configuration. Without clear instructions, it's chaotic. With them, it becomes an extension of your development standards. This article shows how I've configured Claude Code to enforce immutability, follow TDD, automate workflows, and maintain consistency.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; AI tooling moves fast. This reflects my current workflow.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Know What You're Doing First
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Critical:&lt;/strong&gt; Don't blindly trust AI output. You must understand what the code does before shipping it.&lt;/p&gt;

&lt;p&gt;Claude Code is a productivity multiplier, not a replacement for engineering judgment. It automates patterns you already know, enforces standards you've already defined, and catches mistakes you'd catch in review - but faster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Before using these configs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Understand the fundamentals&lt;/strong&gt; - Know why immutability matters, not just that it's a rule&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review every change&lt;/strong&gt; - Read diffs before committing. AI makes mistakes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test everything&lt;/strong&gt; - Run tests, check behavior. Don't assume it works.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Own the output&lt;/strong&gt; - You ship it, you're responsible. AI is a tool, not an excuse.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The configs below work because I know what good code looks like in my context. They encode decisions I've already made. If you copy them without understanding why, you'll ship bad code faster.&lt;/p&gt;

&lt;p&gt;Use AI to accelerate what you already know how to do well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;code&gt;.claude/&lt;/code&gt; Directory
&lt;/h2&gt;

&lt;p&gt;Global config lives at &lt;code&gt;~/.claude/&lt;/code&gt;.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/.claude/
├── CLAUDE.md &lt;span class="c"&gt;# Global instructions&lt;/span&gt;
├── docs/     &lt;span class="c"&gt;# Conventions (git, typescript, testing, etc.)&lt;/span&gt;
├── skills/   &lt;span class="c"&gt;# Custom slash commands&lt;/span&gt;
└── agents/   &lt;span class="c"&gt;# Specialized workflows for specific tasks&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Write Short, Prescriptive Docs
&lt;/h2&gt;

&lt;p&gt;Forget lengthy style guides. Write terse, imperative docs. Examples:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bad:&lt;/strong&gt; "We generally prefer to avoid using &lt;code&gt;any&lt;/code&gt; in TypeScript because it defeats the purpose of type safety."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good:&lt;/strong&gt; "No &lt;code&gt;any&lt;/code&gt; - use &lt;code&gt;unknown&lt;/code&gt; if needed"&lt;/p&gt;

&lt;p&gt;Your docs become the AI's memory. Every session, it reads them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom Skills = Workflow Automation
&lt;/h2&gt;

&lt;p&gt;Skills are slash commands that trigger workflows. Mine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/ship&lt;/code&gt; - Stage all, commit, push&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/create-pr&lt;/code&gt; - Create PR with template&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/coverage&lt;/code&gt; - Check test coverage on unstaged changes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/safe-repo&lt;/code&gt; - Scan for leaked secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Before:&lt;/strong&gt; Manually running commands every deployment&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;# Manual commands&lt;/span&gt;
git add &lt;span class="nt"&gt;-A&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"fix: update validation logic"&lt;/span&gt;
git push
git status  &lt;span class="c"&gt;# verify it worked&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;After:&lt;/strong&gt; &lt;code&gt;/ship&lt;/code&gt; - done&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;# Claude handles: status check, staging, commit with style-matched message, push, verification&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;skills/ship/
└── skill.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The markdown defines the workflow. AI executes it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforce Immutability Through Instructions
&lt;/h2&gt;

&lt;p&gt;I mandate immutability in &lt;code&gt;code-principles.md&lt;/code&gt;:&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;## Immutability (mandatory)&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; No array mutations: &lt;span class="sb"&gt;`push`&lt;/span&gt;, &lt;span class="sb"&gt;`pop`&lt;/span&gt;, &lt;span class="sb"&gt;`splice`&lt;/span&gt;, &lt;span class="sb"&gt;`shift`&lt;/span&gt;, &lt;span class="sb"&gt;`unshift`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No object mutations: &lt;span class="sb"&gt;`obj.key=`&lt;/span&gt;, &lt;span class="sb"&gt;`delete obj.key`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Use: spread &lt;span class="sb"&gt;`[...]`&lt;/span&gt;, &lt;span class="sb"&gt;`slice`&lt;/span&gt;, &lt;span class="sb"&gt;`map`&lt;/span&gt;, destructuring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI catches mutations I'd miss in review.&lt;/p&gt;

&lt;h2&gt;
  
  
  TDD By Default
&lt;/h2&gt;

&lt;p&gt;My &lt;code&gt;testing.md&lt;/code&gt; enforces test-first:&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;## Rules&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; Prefer &lt;span class="sb"&gt;`vi.spyOn`&lt;/span&gt; over &lt;span class="sb"&gt;`vi.mock`&lt;/span&gt; for your own code
&lt;span class="p"&gt;-&lt;/span&gt; Only use &lt;span class="sb"&gt;`vi.mock`&lt;/span&gt; for third-party libraries
&lt;span class="p"&gt;-&lt;/span&gt; Test output must be pristine (zero warnings/errors)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fewer mocks = more confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concision Is a Feature
&lt;/h2&gt;

&lt;p&gt;My global instruction: "Extreme concision in all interactions and commits. Sacrifice grammar for brevity."&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Faster responses&lt;/li&gt;
&lt;li&gt;Less noise&lt;/li&gt;
&lt;li&gt;Commit messages match my style&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Changes
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Immutability enforcement:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI catches mutations I'd miss in review&lt;/li&gt;
&lt;li&gt;Array mutation bugs get caught before commit&lt;/li&gt;
&lt;li&gt;Consistent patterns across codebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Commit consistency:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Commits follow conventional commit style&lt;/li&gt;
&lt;li&gt;Messages match my terse style&lt;/li&gt;
&lt;li&gt;PR descriptions follow template structure&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Code review:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fewer style-related comments&lt;/li&gt;
&lt;li&gt;Less back-and-forth on conventions&lt;/li&gt;
&lt;li&gt;Reviews focus on logic, not formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Invest in docs early&lt;/strong&gt; - They compound. Every session benefits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills &amp;gt; repetition&lt;/strong&gt; - If you do it 3x, make it a skill.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prescriptive &amp;gt; descriptive&lt;/strong&gt; - "Do X" beats "we usually prefer X"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test your instructions&lt;/strong&gt; - AI follows literally. Ambiguity = inconsistency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Version control your config&lt;/strong&gt; - Treat &lt;code&gt;.claude/&lt;/code&gt; like code.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Global config&lt;/span&gt;
~/.claude/CLAUDE.md

&lt;span class="c"&gt;# Project-specific overrides (optional)&lt;/span&gt;
.claude/PROJECT.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;AI reads both. Project overrides global.&lt;/p&gt;

&lt;p&gt;My full config: &lt;a href="https://github.com/helderberto/dotfiles/tree/main/claude/.claude" rel="noopener noreferrer"&gt;github.com/helderberto/dotfiles&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;Consistent code, faster reviews, fewer "why did you do that?" moments. The AI becomes an extension of your standards, not a wildcard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;AI without constraints is unpredictable. With clear docs, custom skills, and enforced standards, it becomes reliable.&lt;/p&gt;

&lt;p&gt;Invest time upfront defining your standards. The AI will enforce them consistently across every session. No more hoping it does the right thing.&lt;/p&gt;

&lt;p&gt;The config is the product. Treat it like code: version control it, test it, refactor it.&lt;/p&gt;

&lt;p&gt;Teach once. Enforce forever.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>productivity</category>
      <category>automation</category>
    </item>
    <item>
      <title>Polymorphism with React</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Sat, 04 Mar 2023 12:54:38 +0000</pubDate>
      <link>https://forem.com/helderberto/polymorphism-with-react-3ld9</link>
      <guid>https://forem.com/helderberto/polymorphism-with-react-3ld9</guid>
      <description>&lt;p&gt;In this article, we will create an example using Polymorphism in React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acceptance Criteria
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;LinkButton&lt;/code&gt; that render an element &lt;code&gt;button&lt;/code&gt; or &lt;code&gt;a&lt;/code&gt; depending on the &lt;code&gt;href&lt;/code&gt; prop&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;LinkButton&lt;/code&gt; component has an optional prop called &lt;code&gt;href&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If the &lt;code&gt;href&lt;/code&gt; is provided, the component should render an &lt;code&gt;a&lt;/code&gt; tag. Otherwise, it should render a &lt;code&gt;button&lt;/code&gt; tag&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solving the Problem without Polymorphism
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LinkButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It solves our problem, but imagine if we need to pass the &lt;code&gt;rest&lt;/code&gt; of props to the component.&lt;/p&gt;

&lt;p&gt;It will involve updating the &lt;code&gt;button&lt;/code&gt; and the &lt;code&gt;a&lt;/code&gt;, like the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LinkButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;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;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for every case you want to add props, you gonna need to maintain both cases, so much work, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving the Problem with Polymorphism
&lt;/h2&gt;

&lt;p&gt;Let's create the same example using Polymorphism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;LinkButton&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Tag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may be asking, and what about the &lt;code&gt;href&lt;/code&gt; being passed to the &lt;code&gt;button&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Since it will be &lt;code&gt;undefined&lt;/code&gt; React will take control to avoid rendering this property to our HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Keeping this approach in mind, you can create a component that handles the logic you want, and it will be easier to give maintenance.&lt;/p&gt;

&lt;p&gt;I hope this tip was useful for you!&lt;/p&gt;

</description>
      <category>healthtech</category>
      <category>privacy</category>
      <category>security</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Integrating TLDR with FZF</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Sun, 26 Feb 2023 10:05:23 +0000</pubDate>
      <link>https://forem.com/helderberto/integrating-tldr-with-fzf-2377</link>
      <guid>https://forem.com/helderberto/integrating-tldr-with-fzf-2377</guid>
      <description>&lt;p&gt;In this post, we gonna dive into &lt;a href="https://formulae.brew.sh/formula/tldr" rel="noopener noreferrer"&gt;TLDR&lt;/a&gt; with &lt;a href="https://formulae.brew.sh/formula/fzf" rel="noopener noreferrer"&gt;fzf&lt;/a&gt; search.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage of TLDR
&lt;/h2&gt;

&lt;p&gt;Go to your favorite terminal, with TLDR enabled, and execute the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tldr brew
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Searching with FZF
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;fzf &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will execute a fuzzy search in the current directory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing a TLDR list to FZF
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tldr &lt;span class="nt"&gt;--list&lt;/span&gt; | fzf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first argument &lt;code&gt;tldr --list&lt;/code&gt; will generate a list of available commands enabled in your command line.&lt;/p&gt;

&lt;p&gt;And when passing with a pipe operator &lt;code&gt;|&lt;/code&gt; it will send the results to &lt;code&gt;fzf&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting an Argument with FZF
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tldr &lt;span class="nt"&gt;--list&lt;/span&gt; | fzf | xargs tldr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you did in the previous step, it will share the list to &lt;code&gt;fzf&lt;/code&gt;, and when you select an option it will send the argument via &lt;code&gt;xargs&lt;/code&gt; to execute with &lt;code&gt;tldr&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Previewing Commands on Searching
&lt;/h2&gt;

&lt;p&gt;It creates a preview window, showing the results from TLDR and passing to the new window via &lt;code&gt;xargs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's try with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tldr &lt;span class="nt"&gt;--list&lt;/span&gt; | fzf &lt;span class="nt"&gt;--preview&lt;/span&gt; &lt;span class="s2"&gt;"tldr {1}"&lt;/span&gt; &lt;span class="nt"&gt;--preview-window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;right,60% | xargs tldr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Bonus: Aliasing
&lt;/h2&gt;

&lt;p&gt;Create an alias to avoid remembering all this stuff:&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;alias &lt;/span&gt;&lt;span class="nv"&gt;tldrf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'tldr --list | fzf --preview "tldr {1}" --preview-window=right,60% | xargs tldr'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source your command line configuration file, in my case it is &lt;code&gt;.zshrc&lt;/code&gt;, and voilá!&lt;/p&gt;

&lt;p&gt;Try your new alias into the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tldrf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>bash</category>
      <category>fzf</category>
      <category>tldr</category>
      <category>brew</category>
    </item>
    <item>
      <title>Effects with React useEffect</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Wed, 22 Feb 2023 10:58:41 +0000</pubDate>
      <link>https://forem.com/helderberto/effects-with-react-useeffect-4c8c</link>
      <guid>https://forem.com/helderberto/effects-with-react-useeffect-4c8c</guid>
      <description>&lt;p&gt;Some practical examples of how to use the &lt;a href="https://beta.reactjs.org/reference/react/useEffect" rel="noopener noreferrer"&gt;React useEffect()&lt;/a&gt; -&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;component mounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;component unmounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt; &lt;span class="c1"&gt;// empty array means it will only run on mounting&lt;/span&gt;

  &lt;span class="c1"&gt;// Separation of concerns&lt;/span&gt;
  &lt;span class="c1"&gt;// This is a good example of how to separate concerns splitting into two useEffects to handle each value&lt;/span&gt;
  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name changed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// only triggers when name changes&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age changed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// only triggers when age changes&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;number&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setAge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  On Mounting
&lt;/h2&gt;

&lt;p&gt;Similar to the legacy &lt;code&gt;componentDidMount&lt;/code&gt; it will trigger on mounting the component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;component mounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;component unmounted&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt; &lt;span class="c1"&gt;// empty array means it will only run on mounting&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  On Changing Values
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name changed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// only triggers when name changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Separation of Concerns
&lt;/h2&gt;

&lt;p&gt;Instead of watching two values at the same useEffect, like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Think in separation of concerns, and split each it will be cleaner and easier for the next person who need to read your code, eg:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Separation of concerns&lt;/span&gt;
&lt;span class="c1"&gt;// This is a good example of how to separate concerns splitting into two useEffects to handle each value&lt;/span&gt;
&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;name changed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;name&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="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// only triggers when name changes&lt;/span&gt;

&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;age changed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;age&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="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt; &lt;span class="c1"&gt;// only triggers when age changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Manipulating the DOM with React Ref</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Wed, 22 Feb 2023 10:57:02 +0000</pubDate>
      <link>https://forem.com/helderberto/manipulating-the-dom-with-react-ref-2a5f</link>
      <guid>https://forem.com/helderberto/manipulating-the-dom-with-react-ref-2a5f</guid>
      <description>&lt;p&gt;When you found something like &lt;code&gt;document.querySelector('.something')&lt;/code&gt; in your React codebase, it's probably a code smell.&lt;/p&gt;

&lt;p&gt;To manipulate the DOM you should consider using "ref" - &lt;a href="https://beta.reactjs.org/reference/react/useRef#manipulating-the-dom-with-a-ref" rel="noopener noreferrer"&gt;React Docs - ref&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After rendering the DOM element, it will enable the DOM node and its methods to be called.&lt;/p&gt;

&lt;p&gt;To ensure the "inputRef.current" will be available at the moment of the trigger "focus()", you can do an "early return" to bail out the effect early, like the following snippet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;inputRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;focus&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;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;inputRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Controlled vs Uncontrolled Elements</title>
      <dc:creator>Helder Burato Berto</dc:creator>
      <pubDate>Fri, 03 Feb 2023 10:27:05 +0000</pubDate>
      <link>https://forem.com/helderberto/controlled-vs-uncontrolled-elements-5513</link>
      <guid>https://forem.com/helderberto/controlled-vs-uncontrolled-elements-5513</guid>
      <description>&lt;p&gt;When working with React, you will notice often used naming called &lt;em&gt;Controlled&lt;/em&gt; and &lt;em&gt;Uncontrolled&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So, what do it mean? Let's dive into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Uncontrolled Element
&lt;/h2&gt;

&lt;p&gt;This type of element is not managed by React.&lt;/p&gt;

&lt;p&gt;In the following example, you have an uncontrolled input element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UncontrolledInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Empty Value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;It wont update the value.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the component isn't doing anything, just printing an empty value &lt;code&gt;Empty value&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can't update, nor delete the value.&lt;/p&gt;

&lt;p&gt;Let's add an event handler for updating the value state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;UncontrolledInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that even if we can update the input now, you have a warning on your browser console. Like the following:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Why is this happening?
&lt;/h3&gt;

&lt;p&gt;Since the default value is not initialized in the &lt;code&gt;useState()&lt;/code&gt;, React will consider the initial value as &lt;code&gt;undefined&lt;/code&gt;, so it means the input still uncontrolled at the first render.&lt;/p&gt;

&lt;p&gt;It only will be controlled when the user starts typing on the input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Controlled Element
&lt;/h2&gt;

&lt;p&gt;The Controlled element is an element that is managed the event and values by React.&lt;/p&gt;

&lt;p&gt;Let's create an input managed by React:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ControlledInput&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Start as an empty string&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;
        &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since you set it as an empty string, it will be a controlled element.&lt;/p&gt;

&lt;p&gt;When the user starts typing in the input, the &lt;code&gt;onChange&lt;/code&gt; event updates the value of &lt;em&gt;name&lt;/em&gt; from &lt;code&gt;''&lt;/code&gt; to a new string.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;It's a common mistake updating &lt;code&gt;uncontrolled&lt;/code&gt; elements to &lt;code&gt;controlled&lt;/code&gt; raising this kind of warning.&lt;/p&gt;

&lt;p&gt;When you understand how it works, it's easier to avoid these issues.&lt;/p&gt;

</description>
      <category>cryptocurrency</category>
      <category>ethereum</category>
      <category>blockchain</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
