<?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: MD Ehsanul Haque Rizvy</title>
    <description>The latest articles on Forem by MD Ehsanul Haque Rizvy (@devrizvy).</description>
    <link>https://forem.com/devrizvy</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%2F1307386%2Fca6d3062-f991-45eb-a81f-364dc2174332.jpg</url>
      <title>Forem: MD Ehsanul Haque Rizvy</title>
      <link>https://forem.com/devrizvy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/devrizvy"/>
    <language>en</language>
    <item>
      <title># Enforcing Code Quality on Every Commit: Husky + Biome in a Node.js/TypeScript Project</title>
      <dc:creator>MD Ehsanul Haque Rizvy</dc:creator>
      <pubDate>Fri, 20 Mar 2026 20:16:12 +0000</pubDate>
      <link>https://forem.com/devrizvy/-enforcing-code-quality-on-every-commit-husky-biome-in-a-nodejstypescript-project-19hd</link>
      <guid>https://forem.com/devrizvy/-enforcing-code-quality-on-every-commit-husky-biome-in-a-nodejstypescript-project-19hd</guid>
      <description>&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This article documents the exact setup used in &lt;strong&gt;&amp;lt;&lt;/strong&gt;&lt;em&gt;project's&amp;gt;_api&lt;/em&gt;* — a Node.js REST API built with Express 5, Prisma, TypeScript, and Bun — to enforce linting and formatting on every Git commit using Husky and Biome.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this covers:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How Git hooks work and why raw hooks are insufficient for team projects&lt;/li&gt;
&lt;li&gt;How Husky solves the version-control problem for hooks&lt;/li&gt;
&lt;li&gt;How Biome replaces ESLint + Prettier in a single tool&lt;/li&gt;
&lt;li&gt;The complete configuration for a TypeScript/Prisma project&lt;/li&gt;
&lt;li&gt;Extending the hook with tests once test files exist&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Git Hooks Work
&lt;/h2&gt;

&lt;p&gt;Git hooks are shell scripts that Git executes automatically at specific lifecycle points. The relevant hooks for enforcing code quality are:&lt;/p&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;Trigger&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pre-commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before the commit is recorded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;commit-msg&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;After the commit message is written&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pre-push&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Before pushing to a remote&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;pre-commit&lt;/code&gt; hook fires before Git writes anything. If the script exits with code &lt;code&gt;1&lt;/code&gt;, Git aborts the commit entirely and nothing is recorded. If it exits with code &lt;code&gt;0&lt;/code&gt;, Git proceeds normally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git commit
    ↓
pre-commit script runs
    ↓
Exit 0 → commit recorded
Exit 1 → commit aborted, working tree unchanged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Problem With Raw Git Hooks
&lt;/h3&gt;

&lt;p&gt;Raw hooks live in &lt;code&gt;.git/hooks/&lt;/code&gt;. This directory is explicitly excluded from version control — it does not exist in the remote repository and is never cloned. This means:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Every developer must manually create and configure hooks after cloning&lt;/li&gt;
&lt;li&gt;Hook scripts cannot be peer-reviewed through PRs&lt;/li&gt;
&lt;li&gt;Hook updates must be communicated and applied manually by every team member&lt;/li&gt;
&lt;li&gt;CI pipelines cannot validate that hooks are properly configured&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This makes raw hooks unreliable in any multi-developer context.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Husky Solves This
&lt;/h2&gt;

&lt;p&gt;Husky uses the Git configuration key &lt;code&gt;core.hooksPath&lt;/code&gt; to redirect Git's hook resolution to a custom directory — &lt;code&gt;.husky/&lt;/code&gt; — which lives in the project root and is tracked by version control.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# What Husky sets under the hood&lt;/span&gt;
git config core.hooksPath .husky
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means the &lt;code&gt;.husky/&lt;/code&gt; directory is committed to the repository, versioned, code-reviewed, and cloned alongside the rest of the project.&lt;/p&gt;

&lt;p&gt;Husky also adds a &lt;code&gt;prepare&lt;/code&gt; lifecycle script to &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&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;"prepare"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"husky"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;prepare&lt;/code&gt; script runs automatically when any developer runs &lt;code&gt;bun install&lt;/code&gt; (or &lt;code&gt;npm install&lt;/code&gt;). This means hooks are configured silently on every fresh clone — no manual steps required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Husky characteristics:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2kb gzipped, zero runtime dependencies&lt;/li&gt;
&lt;li&gt;Supports all 13 client-side Git hooks&lt;/li&gt;
&lt;li&gt;Works with any package manager: npm, pnpm, yarn, bun&lt;/li&gt;
&lt;li&gt;Compatible with monorepos and nested projects&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why Biome Instead of ESLint + Prettier
&lt;/h2&gt;

&lt;p&gt;The traditional approach uses two separate tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ESLint&lt;/strong&gt; for lint rules (detecting code problems)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prettier&lt;/strong&gt; for formatting (enforcing consistent style)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means two config files, two separate passes, two tools to keep in sync, and occasional conflicts when ESLint formatting rules clash with Prettier's output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Biome&lt;/strong&gt; is a single Rust-based tool that handles both. One config file (&lt;code&gt;biome.json&lt;/code&gt;), one command, one pass. It is significantly faster than the JavaScript-based alternatives and produces no conflicts between lint and format rules because they are handled by the same engine.&lt;/p&gt;

&lt;p&gt;For a TypeScript project using Bun, Biome is a natural fit — it is invoked via &lt;code&gt;bunx --bun @biomejs/biome&lt;/code&gt; which runs the native binary directly without spawning a Node.js process.&lt;/p&gt;




&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Installation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Install Husky&lt;/span&gt;
bun add &lt;span class="nt"&gt;--dev&lt;/span&gt; husky

&lt;span class="c"&gt;# Initialise — creates .husky/ and adds prepare script to package.json&lt;/span&gt;
bunx husky init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After initialisation, the project structure gains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-root/
├── .husky/
│   └── pre-commit        ← created by husky init (edit this)
├── biome.json
├── package.json          ← "prepare": "husky" added automatically
└── bun.lock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Pre-Commit Hook
&lt;/h3&gt;

&lt;p&gt;Edit &lt;code&gt;.husky/pre-commit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
bunx &lt;span class="nt"&gt;--bun&lt;/span&gt; @biomejs/biome check &lt;span class="nt"&gt;--write&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Flags explained:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flag&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;check&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Run both linter and formatter in a single pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;--write&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-apply safe fixes (formatting, trivial lint fixes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply to the entire project (respects &lt;code&gt;files.includes&lt;/code&gt; in biome.json)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If Biome encounters lint errors it cannot auto-fix — a real code problem — it exits with code &lt;code&gt;1&lt;/code&gt; and the commit is aborted. If all checks pass (or only auto-fixable issues were found), it exits with code &lt;code&gt;0&lt;/code&gt; and the commit proceeds.&lt;/p&gt;




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

&lt;p&gt;The &lt;code&gt;biome.json&lt;/code&gt; for JoseOcando_api:&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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://biomejs.dev/schemas/2.4.6/schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"vcs"&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;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"clientKind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"useIgnoreFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="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;"files"&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;"includes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"!!**/dist"&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;"formatter"&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;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"indentStyle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tab"&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;"javascript"&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;"formatter"&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;"quoteStyle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"double"&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;"assist"&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;"actions"&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;"source"&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;"organizeImports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&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="nl"&gt;"linter"&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;"rules"&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;"suspicious"&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;"useIterableCallbackReturn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"noExplicitAny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&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;"style"&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;"useImportType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&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;"correctness"&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;"noUnusedFunctionParameters"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"noUnusedImports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&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;Configuration decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vcs.useIgnoreFile: true&lt;/code&gt; — Biome reads &lt;code&gt;.gitignore&lt;/code&gt; and skips ignored files (e.g. &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;src/generated/&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;files.includes: ["!!**/dist"]&lt;/code&gt; — explicitly excludes the compiled output directory&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;indentStyle: "tab"&lt;/code&gt; — tabs for indentation (consistent with the TypeScript config)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;quoteStyle: "double"&lt;/code&gt; — double quotes for JavaScript/TypeScript strings&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noExplicitAny: "off"&lt;/code&gt; — Prisma service functions use &lt;code&gt;payload: any&lt;/code&gt; during schema iteration; this rule is disabled until the schema stabilises&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;noUnusedImports: "off"&lt;/code&gt; — disabled to avoid noise on files that are still being actively developed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;organizeImports: "off"&lt;/code&gt; — import ordering is handled manually to avoid conflicts with Prisma-generated imports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rules should be tightened over time. Starting with fewer rules enforced is preferable to fighting the linter constantly during early development.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the Hook Produces
&lt;/h2&gt;

&lt;h3&gt;
  
  
  On a passing commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;❯ git commit -m "feat: add experience endpoint"
» Running pre-commit hook...
✔ Biome: no issues found (12 files checked)
[main 4f2a1c9] feat: add experience endpoint
 3 files changed, 47 insertions(+)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  On a failing commit
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;❯ git commit -m "feat: add experience endpoint"
» Running pre-commit hook...
src/app/module/Experiance/exp.validation.ts:1:8 lint/correctness/noUnusedImports
  Imported variable 'z' is defined but never used.

✖ Found 1 error.
husky - pre-commit script failed (code 1)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commit is aborted. The working tree is unchanged. The developer fixes the error and commits again.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Default Hook Pitfall
&lt;/h2&gt;

&lt;p&gt;When &lt;code&gt;bunx husky init&lt;/code&gt; runs, it generates a default &lt;code&gt;.husky/pre-commit&lt;/code&gt; that contains:&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;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(or &lt;code&gt;bun test&lt;/code&gt; if Bun is detected)&lt;/p&gt;

&lt;p&gt;If your project has no test files yet, this will immediately fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;bun test v1.3.5
No tests found!
Tests need ".test", "_test_", ".spec" or "_spec_" in the filename
husky - pre-commit script failed (code 1)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is correct behaviour — &lt;code&gt;bun test&lt;/code&gt; exited with code &lt;code&gt;1&lt;/code&gt;, so Husky blocked the commit. But the cause is a missing test suite, not a code problem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resolution:&lt;/strong&gt; Replace the default hook content with a script that runs something immediately useful. Biome is the correct choice here since it is already configured and provides real value from the first commit.&lt;/p&gt;

&lt;p&gt;Do not add &lt;code&gt;bun test&lt;/code&gt; back to the hook until actual test files exist. An empty test suite failing the hook on every commit defeats the purpose of having a hook.&lt;/p&gt;




&lt;h2&gt;
  
  
  Extending the Hook With Tests
&lt;/h2&gt;

&lt;p&gt;Once test files exist, extend &lt;code&gt;.husky/pre-commit&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
bunx &lt;span class="nt"&gt;--bun&lt;/span&gt; @biomejs/biome check &lt;span class="nt"&gt;--write&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
bun &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For an Express/Prisma project, start with tests for utilities that have no database dependencies. These run instantly and require no environment setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended starting points in JoseOcando_api:&lt;/strong&gt;&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;Why it's a good first test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/utils/jwtUtils.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Pure functions, no DB, no HTTP&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/shared/catchAsync.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Wraps a handler, fully synchronous&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/shared/sendResponse.ts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Builds a response object, no side effects&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Example — &lt;code&gt;src/utils/jwtUtils.test.ts&lt;/code&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt; &lt;span class="p"&gt;}&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;bun:test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;jwtUtils&lt;/span&gt; &lt;span class="p"&gt;}&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;./jwtUtils&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test_secret_key_32_chars_minimum&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PAYLOAD&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;VISITOR&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&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="s2"&gt;jwtUtils.generateToken&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="s2"&gt;returns a string with three dot-separated segments&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1h&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="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&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;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&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="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="s2"&gt;jwtUtils.verifyToken&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="s2"&gt;returns the original payload on a valid token&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1h&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verifyToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;PAYLOAD&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&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;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&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="s2"&gt;throws JsonWebTokenError on a malformed token&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;expect&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;jwtUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verifyToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;not.a.valid.jwt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&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="s2"&gt;throws TokenExpiredError on an expired token&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwtUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAYLOAD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0s&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="c1"&gt;// ensure expiry&lt;/span&gt;
    &lt;span class="nf"&gt;expect&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;jwtUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verifyToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SECRET&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toThrow&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;h2&gt;
  
  
  Bypassing the Hook
&lt;/h2&gt;

&lt;p&gt;For work-in-progress commits on feature branches:&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;# Environment variable method&lt;/span&gt;
&lt;span class="nv"&gt;HUSKY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"wip: draft implementation"&lt;/span&gt;

&lt;span class="c"&gt;# Git native flag&lt;/span&gt;
git commit &lt;span class="nt"&gt;--no-verify&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"wip: draft implementation"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both methods skip all hooks entirely. &lt;code&gt;HUSKY=0&lt;/code&gt; is Husky-specific and more explicit about intent. &lt;code&gt;--no-verify&lt;/code&gt; is a Git primitive that bypasses all hooks regardless of whether Husky is involved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Policy:&lt;/strong&gt; Never bypass hooks on &lt;code&gt;main&lt;/code&gt; or any branch that feeds into a deployment pipeline. Bypassing should be limited to local WIP branches that will be cleaned up (rebased or squash-merged) before merging.&lt;/p&gt;




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

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Husky&lt;/td&gt;
&lt;td&gt;Manages Git hook lifecycle; makes hooks version-controlled&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;core.hooksPath&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Git config key Husky sets to redirect hook resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;.husky/pre-commit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shell script executed before every commit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Biome&lt;/td&gt;
&lt;td&gt;Performs lint + format check; exits 1 on unfixable errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;prepare&lt;/code&gt; script&lt;/td&gt;
&lt;td&gt;Runs &lt;code&gt;husky&lt;/code&gt; on &lt;code&gt;bun install&lt;/code&gt;; installs hooks on every clone&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The complete hook for a project with tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
bunx &lt;span class="nt"&gt;--bun&lt;/span&gt; @biomejs/biome check &lt;span class="nt"&gt;--write&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
bun &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a project without tests yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
bunx &lt;span class="nt"&gt;--bun&lt;/span&gt; @biomejs/biome check &lt;span class="nt"&gt;--write&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;bun test&lt;/code&gt; to the hook only when test files exist. A hook that always fails provides no value and trains developers to bypass it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Stack: Express 5 · Prisma 7 · TypeScript 5.9 · Bun 1.3 · Biome 2.4 · Husky 9&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>node</category>
      <category>tooling</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Next.js Rendering (Cooking Analogy)</title>
      <dc:creator>MD Ehsanul Haque Rizvy</dc:creator>
      <pubDate>Tue, 24 Feb 2026 21:12:49 +0000</pubDate>
      <link>https://forem.com/devrizvy/nextjs-rendering-cooking-analogy-466n</link>
      <guid>https://forem.com/devrizvy/nextjs-rendering-cooking-analogy-466n</guid>
      <description>&lt;p&gt;Next.js lets you control when and where rendering happens.&lt;/p&gt;




&lt;h1&gt;
  
  
  1. Client-Side Rendering (CSR) — browser cooks everything
&lt;/h1&gt;

&lt;p&gt;This is the classic React behavior.&lt;/p&gt;

&lt;p&gt;You visit &lt;code&gt;/products&lt;/code&gt;, and the browser gets an empty shell first. Then JavaScript runs, makes a request, and fills the data.&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&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="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;ProductsPage&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;products&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setProducts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/products&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;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;setProducts&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="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New Products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;Timeline of reality:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser loads page
&lt;/li&gt;
&lt;li&gt;Page is empty or loading
&lt;/li&gt;
&lt;li&gt;JS runs
&lt;/li&gt;
&lt;li&gt;JS fetches data
&lt;/li&gt;
&lt;li&gt;UI updates
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;User waits longer. This is slower.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;User dashboards
&lt;/li&gt;
&lt;li&gt;Interactive UI
&lt;/li&gt;
&lt;li&gt;Forms
&lt;/li&gt;
&lt;li&gt;User-specific content
&lt;/li&gt;
&lt;li&gt;When SEO is not important
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When NOT to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public pages
&lt;/li&gt;
&lt;li&gt;Product listings
&lt;/li&gt;
&lt;li&gt;Blogs
&lt;/li&gt;
&lt;li&gt;SEO pages
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  2. Server-Side Rendering (SSR) — server cooks on request
&lt;/h1&gt;

&lt;p&gt;The browser requests &lt;code&gt;/products&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The server immediately fetches products and sends fully prepared HTML.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductsPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/products&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="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;New Products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;Timeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Browser requests page
&lt;/li&gt;
&lt;li&gt;Server fetches products
&lt;/li&gt;
&lt;li&gt;Server builds HTML with products already inside
&lt;/li&gt;
&lt;li&gt;Browser receives ready page instantly
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nothing loads afterward.&lt;/p&gt;

&lt;p&gt;SSR = fresh data every request.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Live inventory
&lt;/li&gt;
&lt;li&gt;Stock prices
&lt;/li&gt;
&lt;li&gt;User-specific pages
&lt;/li&gt;
&lt;li&gt;Data that must always be fresh
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When NOT to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Static content
&lt;/li&gt;
&lt;li&gt;Blogs
&lt;/li&gt;
&lt;li&gt;Marketing pages
&lt;/li&gt;
&lt;li&gt;Data that rarely changes
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  3. Static Site Generation (SSG) — server cooks once at build time
&lt;/h1&gt;

&lt;p&gt;Next.js fetches data during build and freezes it.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductsPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/products&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="na"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Popular Products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;Timeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You run &lt;code&gt;npm run build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Next.js fetches products
&lt;/li&gt;
&lt;li&gt;Next.js saves ready HTML
&lt;/li&gt;
&lt;li&gt;Every visitor gets that same HTML instantly
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fastest rendering method.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Blogs
&lt;/li&gt;
&lt;li&gt;Landing pages
&lt;/li&gt;
&lt;li&gt;Documentation
&lt;/li&gt;
&lt;li&gt;Marketing pages
&lt;/li&gt;
&lt;li&gt;Content that rarely changes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When NOT to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live dashboards
&lt;/li&gt;
&lt;li&gt;Frequently changing data
&lt;/li&gt;
&lt;li&gt;User-specific data
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  4. Incremental Static Regeneration (ISR) — server cooks occasionally
&lt;/h1&gt;

&lt;p&gt;Next.js builds the page and updates it later automatically.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ProductsPage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://api.example.com/products&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="na"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&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;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Products&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&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;products&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&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;h5&gt;
  
  
  Timeline:
&lt;/h5&gt;

&lt;p&gt;Build time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Products fetched
&lt;/li&gt;
&lt;li&gt;Page saved
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;User visits within 60 seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gets cached version instantly
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After 60 seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First visitor still gets old version
&lt;/li&gt;
&lt;li&gt;Next.js refreshes in background
&lt;/li&gt;
&lt;li&gt;Future visitors get updated version
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Product listings
&lt;/li&gt;
&lt;li&gt;News sites
&lt;/li&gt;
&lt;li&gt;Ecommerce pages
&lt;/li&gt;
&lt;li&gt;Content that changes sometimes
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When NOT to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time data
&lt;/li&gt;
&lt;li&gt;User-specific private data
&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  The Real Control Switch
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;         &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;SSR&lt;/span&gt;  
&lt;span class="nx"&gt;cache&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;force-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;      &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;SSG&lt;/span&gt;  
&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;revalidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nx"&gt;ISR&lt;/span&gt;  
&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;              &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nc"&gt;CSR &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;fetching&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"use client" controls WHERE code runs.&lt;/p&gt;

&lt;p&gt;Not WHEN data is fetched.&lt;/p&gt;

&lt;p&gt;"use client" → browser&lt;br&gt;&lt;br&gt;
no directive → server  &lt;/p&gt;




&lt;h1&gt;
  
  
  Performance Order (Fastest to Slowest)
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;SSG
&lt;/li&gt;
&lt;li&gt;ISR
&lt;/li&gt;
&lt;li&gt;SSR
&lt;/li&gt;
&lt;li&gt;CSR
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Work done earlier is always faster than work done later.&lt;/p&gt;




&lt;h1&gt;
  
  
  Final Mental Model
&lt;/h1&gt;

&lt;p&gt;CSR → browser fetches after load  &lt;/p&gt;

&lt;p&gt;SSR → server fetches on every request  &lt;/p&gt;

&lt;p&gt;SSG → server fetches at build time  &lt;/p&gt;

&lt;p&gt;ISR → server fetches at build time and refreshes later  &lt;/p&gt;

&lt;p&gt;"use client" → browser execution  &lt;/p&gt;

&lt;p&gt;no "use client" → server execution&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>Terminal, Shell, tmux: What They Really Mean (And How to Use Them as a Newbie)</title>
      <dc:creator>MD Ehsanul Haque Rizvy</dc:creator>
      <pubDate>Tue, 10 Feb 2026 19:13:41 +0000</pubDate>
      <link>https://forem.com/devrizvy/terminal-shell-tmux-what-they-really-mean-and-how-to-use-them-as-a-newbie-2a6n</link>
      <guid>https://forem.com/devrizvy/terminal-shell-tmux-what-they-really-mean-and-how-to-use-them-as-a-newbie-2a6n</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;The command line stacks up in layers: Terminal emulator (the display window), Shell (the command interpreter), Programs (the tools you run), and optional tmux (session manager for multitasking). Beginners should start with defaults like iTerm2 and zsh on macOS, or Windows Terminal with WSL on Windows. Focus on basics before customizing to avoid confusion.&lt;/p&gt;

&lt;p&gt;Ever stared at your screen, typing commands into a black box, and wondered why terms like &lt;strong&gt;iTerm2&lt;/strong&gt;, &lt;strong&gt;zsh&lt;/strong&gt;, or &lt;strong&gt;tmux&lt;/strong&gt; get thrown around like they're interchangeable? Spoiler: They're not. As a developer who once rage-quit a terminal session thinking my "window" was broken (turns out, it was just a misconfigured shell), I know the frustration. This guide breaks it down into clear layers. No jargon overload, just clarity. By the end, you'll navigate the command line with confidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Three Core Layers of the Command Line (Plus One Bonus)
&lt;/h2&gt;

&lt;p&gt;Imagine the command line as a &lt;strong&gt;cake stack&lt;/strong&gt;. Each layer builds on the last, but they serve distinct purposes. Mixing them up is like confusing the frosting with the sponge. Messy! Here's a simple visual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------+
|   Programs        |  (e.g., git, node, vim)
|                   |
+-------------------+
|   Shell           |  (The brain: bash, zsh, etc.)
|                   |
+-------------------+
| Terminal Emulator |  (The window: iTerm2, Kitty, etc.)
+-------------------+
(Optional: tmux slots in between Shell and Programs for session magic)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This modular setup makes everything predictable. Let's dive in.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 1: Terminal Emulator. The Window You See
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;terminal emulator&lt;/strong&gt; is just the app that gives you a blank window for typing. It doesn't "think". It displays text output and sends your keystrokes to the next layer. Think of it like a TV screen. It shows the show but doesn't write the script.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Terminal Emulators&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS Terminal&lt;/strong&gt; (built-in, simple)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;iTerm2&lt;/strong&gt; (feature-packed for Mac users)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ghostty&lt;/strong&gt; (modern, minimalist)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kitty&lt;/strong&gt; (fast, GPU-accelerated)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WezTerm&lt;/strong&gt; (cross-platform, scriptable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alacritty&lt;/strong&gt; (performance-focused)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows Terminal&lt;/strong&gt; (modern Windows default)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GNOME Terminal&lt;/strong&gt; or &lt;strong&gt;Konsole&lt;/strong&gt; (Linux staples)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros and Cons Quick Table&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Emulator&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;iTerm2&lt;/td&gt;
&lt;td&gt;Tabs, splits, macOS integration&lt;/td&gt;
&lt;td&gt;Mac-only, can be overwhelming&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kitty&lt;/td&gt;
&lt;td&gt;Speed, image support&lt;/td&gt;
&lt;td&gt;Steeper learning curve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Alacritty&lt;/td&gt;
&lt;td&gt;Blazing fast, lightweight&lt;/td&gt;
&lt;td&gt;Fewer built-in features&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When you type &lt;code&gt;ls&lt;/code&gt;, the emulator blindly passes it along and shows the result. No smarts here.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try It&lt;/strong&gt;: Open your default terminal and type &lt;code&gt;echo "Hello, World!"&lt;/code&gt;. See? The window just echoes back. It's not interpreting anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Pitfall&lt;/strong&gt;: If fonts look weird or colors are off, tweak the emulator's settings, not your shell config.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: The Shell. The Brain That Interprets Commands
&lt;/h3&gt;

&lt;p&gt;Now we're at the smarts. The &lt;strong&gt;shell&lt;/strong&gt; reads your input, understands commands, and talks to the OS. It's like the director telling actors (programs) what to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Shells&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bash&lt;/strong&gt; (Bourne Again Shell): Reliable, default on many Linux systems. Great for scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;zsh&lt;/strong&gt; (Z Shell): Bash with extras like better autocompletion. macOS default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;fish&lt;/strong&gt; (Friendly Interactive Shell): User-friendly with smart suggestions, but non-standard scripting.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sh&lt;/strong&gt; (basic, lightweight Bourne Shell).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PowerShell&lt;/strong&gt;: Microsoft's object-oriented powerhouse. Passes data as objects, not text.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pros and Cons Quick Table&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Shell&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;bash&lt;/td&gt;
&lt;td&gt;Ubiquitous, tutorial-friendly&lt;/td&gt;
&lt;td&gt;Basic out-of-the-box&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;zsh&lt;/td&gt;
&lt;td&gt;Plugins, themes (e.g., Oh My Zsh)&lt;/td&gt;
&lt;td&gt;Slightly heavier than bash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;fish&lt;/td&gt;
&lt;td&gt;Intuitive, no config needed&lt;/td&gt;
&lt;td&gt;Incompatible with bash scripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PowerShell&lt;/td&gt;
&lt;td&gt;Structured data handling&lt;/td&gt;
&lt;td&gt;Less common in Unix tutorials&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Personal Story&lt;/strong&gt;: Early on, I customized my prompt in &lt;code&gt;~/.zshrc&lt;/code&gt; and thought my terminal was "fancy." Nope. That's shell magic! Edits here change prompts, aliases, and paths, not the window itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try It&lt;/strong&gt;: Type &lt;code&gt;which ls&lt;/code&gt; in your shell. It tells you where the &lt;code&gt;ls&lt;/code&gt; command lives. Proof the shell is interpreting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Pitfall&lt;/strong&gt;: Aliases or prompts not loading? Check your shell config file (e.g., &lt;code&gt;~/.bashrc&lt;/code&gt; for bash). Switching shells? Use &lt;code&gt;chsh&lt;/code&gt; carefully to avoid lockouts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: Programs. The Tools You Actually Run
&lt;/h3&gt;

&lt;p&gt;Inside the shell, you launch &lt;strong&gt;programs&lt;/strong&gt; like everyday apps. Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;git clone repo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;node server.js&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python script.py&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;vim file.txt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker run container&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The flow: Emulator shows the window. Shell interprets. Program runs. Output displays.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try It&lt;/strong&gt;: Run &lt;code&gt;git --version&lt;/code&gt;. If it's not installed, the shell will complain. Not the emulator.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Pitfall&lt;/strong&gt;: Program errors (e.g., "command not found") are often path issues in your shell config, not a "broken terminal."&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus Layer: tmux. The Session Manager Superhero
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;tmux&lt;/strong&gt; (Terminal Multiplexer) isn't an emulator or shell. It's a tool that runs &lt;em&gt;inside&lt;/em&gt; your shell to manage sessions. Perfect for remote work or multitasking.&lt;/p&gt;

&lt;p&gt;What it does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Split windows into panes.&lt;/li&gt;
&lt;li&gt;Detach/reattach sessions (e.g., keep a script running after logout).&lt;/li&gt;
&lt;li&gt;Handle multiple tasks without closing your terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Updated Stack Visual:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------+
|   Programs        |
|                   |
+-------------------+
|   tmux            |  (Optional: Panes &amp;amp; sessions)
|                   |
+-------------------+
|   Shell           |
|                   |
+-------------------+
| Terminal Emulator |
+-------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros and Cons&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pros: Persistent sessions, great for servers.&lt;/li&gt;
&lt;li&gt;Cons: Learning curve (keybindings like Ctrl+b).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Try It (If Installed)&lt;/strong&gt;: Run &lt;code&gt;tmux new -s mysession&lt;/code&gt;, then detach with Ctrl+b d. Reattach with &lt;code&gt;tmux attach -t mysession&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Common Pitfall&lt;/strong&gt;: Sessions vanishing? tmux isn't automatic. Start it manually.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beginner Recommendations: Start Simple, Scale Later
&lt;/h2&gt;

&lt;p&gt;Don't overcomplicate day one. Focus on basics like &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ls&lt;/code&gt;, &lt;code&gt;mkdir&lt;/code&gt;, and Git.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;macOS&lt;/strong&gt;: iTerm2 + zsh (default). Skip tmux.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux&lt;/strong&gt;: Default emulator + bash. Upgrade to zsh later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows&lt;/strong&gt;: Windows Terminal + WSL (Ubuntu with bash/zsh) for Unix compatibility. Or pure PowerShell if you're in Microsoft ecosystems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Customization comes after mastery. Avoid plugin overload.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ: Quick Answers to Common Questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;How do I change my shell?&lt;/strong&gt; Use &lt;code&gt;chsh -s /bin/zsh&lt;/code&gt; (check paths with &lt;code&gt;which zsh&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terminal slow?&lt;/strong&gt; Try a performant emulator like Alacritty.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why learn this?&lt;/strong&gt; It unlocks efficient dev workflows, automation, and server management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where next?&lt;/strong&gt; Practice with freeCodeCamp's CLI module or "The Missing Semester" course.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>discuss</category>
      <category>devops</category>
      <category>beginners</category>
      <category>productivity</category>
    </item>
    <item>
      <title>🧙‍♂️ Stop Using Prettier and ESLint Separately | Biome Handles It Better</title>
      <dc:creator>MD Ehsanul Haque Rizvy</dc:creator>
      <pubDate>Wed, 12 Nov 2025 19:16:11 +0000</pubDate>
      <link>https://forem.com/devrizvy/stop-using-prettier-and-eslint-separately-biome-handles-it-better-4kpo</link>
      <guid>https://forem.com/devrizvy/stop-using-prettier-and-eslint-separately-biome-handles-it-better-4kpo</guid>
      <description>&lt;p&gt;I’ve been using Prettier since I first started learning programming. Back then, it was all HTML, CSS, and a few frameworks then came React, Express, and Next.js. And honestly, I’ve always liked my code clean and tidy (maybe a little too much 😆).&lt;/p&gt;

&lt;p&gt;In the early days, I formatted everything manually with  and VS Code’s default formatter. But the more I learned and the more projects I worked on, the harder it became to keep my code consistently formatted that way. So, naturally, I started using Prettier, and later added ESLint to keep my syntax sharp and consistent. I stuck with that setup for a long time it worked, but it was never seamless.&lt;/p&gt;

&lt;p&gt;Then one day, I stumbled upon a blog post about a new tool that could format and lint at the same time. That got my attention. I gave it a try without much thought, and… guess what? Totally worth it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Biome&lt;/strong&gt; turned out to be way faster than Prettier and comes with a built-in linter that works just as smoothly as ESLint. It’s compact, fast, and surprisingly reliable. No extra configuration chaos. So if you’re thinking about trying Biome, here’s how you can get started it’s super simple.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Biome :
&lt;/h2&gt;

&lt;p&gt;You’ll need to install it as a dev dependency. I’m using &lt;br&gt;
&lt;code&gt;Bun&lt;/code&gt;, but you can use &lt;code&gt;npm, pnpm, yarn, or even Deno&lt;/code&gt; Biome supports them all.&lt;br&gt;
And honestly, their docs are excellent very readable and beginner-friendly. But let’s go through the basics right here.&lt;/p&gt;

&lt;p&gt;Install Biome&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bun add -D -E @biomejs/biome
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize Biome&lt;br&gt;
This creates a biome.json file where you can configure everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bunx --bun biome init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it! You’re ready to use Biome no need to install Prettier and ESLint separately anymore.&lt;/p&gt;

&lt;p&gt;⚡ Basic Commands&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Format all files:
bunx biome format --write

Format specific files:
bunx biome format --write &amp;lt;files&amp;gt;

Lint and apply safe fixes to all files:
bunx biome lint --write

Lint specific files:
bunx biome lint --write &amp;lt;files&amp;gt;

Format, lint, and organize imports (all files):
bunx biome check --write

Format, lint, and organize imports (specific files):
bunx biome check --write &amp;lt;files&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ice12c6h4ipjc7qwfpd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8ice12c6h4ipjc7qwfpd.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: Editor Support
&lt;/h2&gt;

&lt;p&gt;Biome also has extensions for popular editors like VS Code, IntelliJ, and Zed&lt;br&gt;
Plus, it supports modern frameworks like Svelte, Vue, and Astro right out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If you’re tired of juggling Prettier and ESLint configs, give Biome v2 a shot. It’s fast, clean, and takes care of formatting, linting, and import organization in one go. No more mismatched configs just a smoother, faster developer experience.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>frontend</category>
      <category>biome</category>
      <category>react</category>
    </item>
    <item>
      <title>🚀 Customizing GitHub Copilot &amp; GitLens for AI-Assisted Commit Messages 🧠💡</title>
      <dc:creator>MD Ehsanul Haque Rizvy</dc:creator>
      <pubDate>Tue, 04 Mar 2025 17:05:47 +0000</pubDate>
      <link>https://forem.com/devrizvy/customizing-github-copilot-gitlens-for-ai-assisted-commit-messages-485j</link>
      <guid>https://forem.com/devrizvy/customizing-github-copilot-gitlens-for-ai-assisted-commit-messages-485j</guid>
      <description>&lt;p&gt;Commit messages are essential for maintaining a clean, understandable project history. With GitHub Copilot 🤖 and GitLens 🔍, you can automate commit message generation with AI assistance, ensuring they are concise, meaningful, and well-structured.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Why Automate Commit Messages?
&lt;/h2&gt;

&lt;p&gt;Commit messages should be clear and concise 📝. They provide context for changes and enhance team collaboration. But writing good messages every time? Tedious! 😵‍💫&lt;/p&gt;

&lt;p&gt;This is where GitHub Copilot and GitLens step in! 🚀 These AI-powered tools help automate commit message generation while following best practices ✅.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Tools You'll Need:
&lt;/h2&gt;

&lt;p&gt;🔹 GitHub Copilot – Powered by OpenAI’s GPT-4, suggests and generates commit messages based on your changes.&lt;br&gt;
🔹 GitLens – A powerful Git extension for Visual Studio Code, enhancing your Git workflow with AI-generated commit messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Setting Up GitHub Copilot &amp;amp; GitLens
&lt;/h2&gt;

&lt;p&gt;1️⃣ &lt;strong&gt;Install GitHub Copilot 🤖&lt;/strong&gt;&lt;br&gt;
1️⃣ Open VSCode.&lt;br&gt;
2️⃣ Navigate to the Extensions view&lt;code&gt;(Ctrl + Shift + X or Cmd + Shift + X)&lt;/code&gt;.&lt;br&gt;
3️⃣ Search for "GitHub Copilot" and install it.&lt;br&gt;
4️⃣ Sign in with your GitHub account.&lt;/p&gt;

&lt;p&gt;2️⃣ &lt;strong&gt;Install GitLens 🔍&lt;/strong&gt;&lt;br&gt;
1️⃣ Open VSCode.&lt;br&gt;
2️⃣ Go to the Extensions view.&lt;br&gt;
3️⃣ Search for "GitLens" and install it.&lt;br&gt;
4️⃣ GitLens will automatically integrate with your Git workflow in VSCode.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Configure Commit Message Generation ⚡️
&lt;/h2&gt;

&lt;p&gt;3️⃣ Create &lt;code&gt;.copilot-commit-message-instructions.md&lt;/code&gt; 📜&lt;br&gt;
This file provides commit message rules for GitHub Copilot. Place it in the root of your project.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;**📌 Example Instructions:&lt;/em&gt;*&lt;/p&gt;

&lt;h1&gt;
  
  
  GitHub Copilot Commit Message Instructions 🚀
&lt;/h1&gt;

&lt;h2&gt;
  
  
  General Guidelines:
&lt;/h2&gt;

&lt;p&gt;✅ Use the &lt;strong&gt;imperative mood&lt;/strong&gt; (e.g., "Add feature" instead of "Added feature").&lt;br&gt;&lt;br&gt;
✅ Be &lt;strong&gt;concise and clear&lt;/strong&gt; – avoid overly detailed explanations.&lt;br&gt;&lt;br&gt;
✅ Follow standard commit types:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feat&lt;/code&gt;: 🚀 For new features
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fix&lt;/code&gt;: 🐛 For bug fixes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refactor&lt;/code&gt;: 🔄 For restructuring code without behavior changes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;style&lt;/code&gt;: 🎨 For cosmetic changes
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;perf&lt;/code&gt;: ⚡ For performance improvements
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;test&lt;/code&gt;: 🧪 For adding/updating tests
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📌 &lt;strong&gt;Examples:&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;feat(auth): add two-factor authentication 🔐&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fix(ui): correct button hover effect 🎨&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refactor(api): optimize database queries 🚀&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;style(css): update button styles 💅&lt;/code&gt; **&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4️⃣ Configure&lt;code&gt;settings.json&lt;/code&gt; ⚙️
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;"git.enableSmartCommit": true&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔍 Test &amp;amp; Verify Commit Message Generation 🎯
&lt;/h2&gt;

&lt;p&gt;1️⃣ Make a change (e.g., add a feature or fix a bug).&lt;br&gt;
2️⃣ Open Source Control&lt;code&gt;(Ctrl + Shift + G / Cmd + Shift + G)&lt;/code&gt;.&lt;br&gt;
3️⃣ Commit your changes – Copilot will suggest a commit message based on the changes.&lt;br&gt;
4️⃣ Review &amp;amp; refine the message if necessary.&lt;/p&gt;

&lt;p&gt;If commit messages don’t meet expectations, tweak &lt;code&gt;.copilot-commit-message-instructions.md&lt;/code&gt;✍️.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛠 Troubleshooting Tips 🔧
&lt;/h2&gt;

&lt;p&gt;🔹 No commit messages? – Ensure .copilot-commit-message-instructions.md is in the root of your project.&lt;br&gt;
🔹 Messages too verbose? – Simplify the instructions to focus on clarity and conciseness.&lt;br&gt;
🔹 VSCode doesn’t suggest commits? – Restart VSCode and check Copilot authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Conclusion
&lt;/h2&gt;

&lt;p&gt;By integrating GitHub Copilot 🤖 and GitLens 🔍, you can automate commit messages, making them consistent and efficient. This saves time, reduces human error, and keeps your commit history clean &amp;amp; structured 📜.&lt;/p&gt;

&lt;p&gt;Now, every commit tells a story—without the extra effort! 🎯🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>🌟 Master the Code Seas with Windsurf IDE 🌟</title>
      <dc:creator>MD Ehsanul Haque Rizvy</dc:creator>
      <pubDate>Thu, 21 Nov 2024 05:33:57 +0000</pubDate>
      <link>https://forem.com/devrizvy/master-the-code-seas-with-windsurf-ide-5cad</link>
      <guid>https://forem.com/devrizvy/master-the-code-seas-with-windsurf-ide-5cad</guid>
      <description>&lt;p&gt;If you haven’t tried Windsurf, you’re missing out on an IDE that feels like smooth sailing through your codebase! ⛵ Designed to make your dev life effortless, it’s fast, intuitive, and packed with features you’ll love:&lt;/p&gt;

&lt;p&gt;🔥 Blazing Fast Performance: Perfect for handling even the heaviest projects.&lt;br&gt;
💻 Seamless Multi-language Support: Switch between your favorite languages effortlessly.&lt;br&gt;
🎨 Customizable Workspace: Tailor your setup to match your workflow.&lt;/p&gt;

&lt;p&gt;🌊 Enhance Windsurf with Codeium!&lt;br&gt;
Want to level up? I integrated Codeium with Windsurf, and it’s been a total game-changer:&lt;/p&gt;

&lt;p&gt;Real-time AI suggestions&lt;br&gt;
On-the-fly bug fixing&lt;br&gt;
Multi-language AI support&lt;br&gt;
🌬️ Together, Windsurf + Codeium feels like catching the perfect coding wave!&lt;/p&gt;

&lt;p&gt;🚀 Ready to ride the Windsurf wave? Check it out and let me know your thoughts below!&lt;/p&gt;

&lt;h1&gt;
  
  
  Coding #WindsurfIDE #DeveloperTools #Productivity #DevLife
&lt;/h1&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>programming</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
