<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kev</title>
    <description>The latest articles on DEV Community by Kev (@graezykev).</description>
    <link>https://dev.to/graezykev</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%2F1425699%2F36981583-6544-43e8-8660-1842a1f5941d.jpeg</url>
      <title>DEV Community: Kev</title>
      <link>https://dev.to/graezykev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/graezykev"/>
    <language>en</language>
    <item>
      <title>Dev Containers - Part 5: Multiple Projects &amp; Shared Container Configuration</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 21 Jun 2024 06:46:15 +0000</pubDate>
      <link>https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi</link>
      <guid>https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi</guid>
      <description>&lt;p&gt;Welcome to the fifth guide in the Dev Container series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727"&gt;Dev Containers - Why You Need Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;Part 1: Quick Start - Basic Setup and Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Part 2: Image, Features, Workspace, Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Part 3: Full Stack Dev - Docker Compose &amp;amp; Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l"&gt;Part 4: Remote Dev - Develop on a Remote Docker Host&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 5: Multiple Projects &amp;amp; Shared Container Configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started, you can clone &lt;a href="https://github.com/graezykev/dev-container/tree/part-5-shared-configure-for-multiple-projects" rel="noopener noreferrer"&gt;my demo project&lt;/a&gt; using the following command:&lt;/p&gt;

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

git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-5-shared-configure-for-multiple-projects https://github.com/graezykev/dev-container.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Part 3&lt;/a&gt;, we learned about using Docker Compose in Dev Containers to build containers for &lt;code&gt;Node.js&lt;/code&gt; applications and databases.&lt;/p&gt;

&lt;p&gt;Currently, you can only connect to one container per Visual Studio Code window. But what if you have multiple projects using different tech stacks like &lt;code&gt;Node.js&lt;/code&gt;, &lt;code&gt;Python&lt;/code&gt;, &lt;code&gt;Go&lt;/code&gt;, etc., and need to create Dev Containers for each?&lt;/p&gt;

&lt;p&gt;One option is to place a &lt;code&gt;.devcontainer&lt;/code&gt; folder under each project:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
└── path
 └── to
    ├── project-a-node-js
    │   └── .devcontainer
    │       ├── docker-compose.yml
    │       ├── ...
    │       └── devcontainer.json
    ├── project-b-node-js
    │   └── .devcontainer
    │       ├── ...
    │       └── devcontainer.json
    ├── project-c-python
    │   └── .devcontainer
    │       ├── ...
    │       └── devcontainer.json
    ├── project-d-go-lang
    │   └── .devcontainer
    │       ├── ...
    │       └── devcontainer.json
    └── project-...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If these applications need to share the same database, you must ensure they all use the same database container in their &lt;code&gt;docker-compose.yml&lt;/code&gt; and the same volume:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="s"&gt;app-name-...&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;postgres&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

&lt;span class="nn"&gt;...&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However, this can result in overlapping configurations across multiple projects, making maintenance tedious. A better approach is to share a common &lt;code&gt;docker-compose.yml&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
└── path
 └── to
  └── dev-container
      │
      ├── .devcontainer
      │   │
      │   ├── .env
      │   ├── docker-compose.yml
      │   ├── ...
      │   │
      │   ├── project-a-node-js
      │   │   └── devcontainer.json
      │   │
      │   ├── project-b-node-js
      │   │   └── devcontainer.json
      │   │
      │   ├── project-c-python
      │   │   └── devcontainer.json
      │   │
      │   ├── project-d-go-lang
      │   │   └── devcontainer.json
      │   │
      │   └── project-e-...
      │       └── devcontainer.json
      │
      ├── project-a-node-js
      │       └── index.js
      │
      ├── project-b-node-js
      │
      ├── project-c-python
      │       └── hello.py
      │
      ├── project-d-go-lang
      └── project-e-...


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;All projects and the &lt;code&gt;.devcontainer&lt;/code&gt; folder share a common root-level folder, with each project having its own configuration folder under &lt;code&gt;.devcontainer&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This setup allows you to define multiple Dev Containers (and a container for the database) in a common &lt;code&gt;docker-compose.yml&lt;/code&gt;, and create a &lt;code&gt;devcontainer.json&lt;/code&gt; for each project to reference the shared &lt;code&gt;docker-compose.yml&lt;/code&gt;. This approach also helps manage each project's features and lifecycle scripts, avoiding configuration conflicts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Common Docker Compose File
&lt;/h2&gt;

&lt;p&gt;First, create a common &lt;code&gt;docker-compose.yml&lt;/code&gt; inside the root-level &lt;code&gt;.devcontainer&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;project-a-node-js&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;graezykev/dev-container-base-image:latest&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;..:/workspaces:cached&lt;/span&gt;
    &lt;span class="s"&gt;ports&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8001:8000&lt;/span&gt;
    &lt;span class="s"&gt;depends_on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="s"&gt;command&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/bin/zsh -c "while sleep 1000; do :; done"&lt;/span&gt;

  &lt;span class="s"&gt;project-b-node-js&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;volumes&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;..:/workspaces:cached&lt;/span&gt;
    &lt;span class="s"&gt;ports&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8002:8000&lt;/span&gt;

  &lt;span class="s"&gt;project-c-python&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;project-d-go-lang&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

  &lt;span class="s"&gt;project-e-...&lt;/span&gt;

  &lt;span class="s"&gt;postgres&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;See the complete file in &lt;a href="https://github.com/graezykev/dev-container/blob/part-5-shared-configure-for-multiple-projects/.devcontainer/docker-compose.yml" rel="noopener noreferrer"&gt;my demo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All projects share the same database container &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We have learned most of the concepts in &lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o#1-define-raw-services-endraw-"&gt;Part 3&lt;/a&gt; but there are a few things we need to pay attention to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ports
&lt;/h3&gt;

&lt;p&gt;Notice the port mappings: &lt;code&gt;8001:8000&lt;/code&gt;, &lt;code&gt;8002:8000&lt;/code&gt;, etc. Each project uses port &lt;code&gt;8000&lt;/code&gt; within its own Dev Container, mapped to different ports on the host machine. This setup avoids port conflicts and allows access to each project's server via distinct ports (e.g., &lt;code&gt;8001&lt;/code&gt;, &lt;code&gt;8002&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;See demo preveiw below.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Volumes &amp;amp; Workspace
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;..:/workspaces&lt;/code&gt; for the &lt;code&gt;volumes&lt;/code&gt; sections mounts the entire root-level folder (&lt;code&gt;dev-container&lt;/code&gt;) on the host machine to &lt;code&gt;/workspaces&lt;/code&gt; in the containers.&lt;/p&gt;

&lt;p&gt;In each &lt;code&gt;devcontainer.json&lt;/code&gt;, specify the project folder within the workspace:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/project-b-node-js"&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flescp4dh5oq5x69flh13.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flescp4dh5oq5x69flh13.png" alt="set workspaceFolder for the right dev container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Service
&lt;/h3&gt;

&lt;p&gt;In each &lt;code&gt;devcontainer.json&lt;/code&gt;, reference the Docker Compose file &lt;code&gt;docker-compose.yml&lt;/code&gt; and specify the service name:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dev Container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dockerComposeFile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"../docker-compose.yml"&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;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project-a-node-js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shutdownAction"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"none"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/project-a-node-js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project-b-node-js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/project-b-node-js"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"service"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"project-c-python"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/project-c-python"&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;"shutdownAction": "none"&lt;/code&gt; option will leave the containers running when VS Code closes -- which prevents you from accidentally shutting down both containers by closing one window (or switching containers).&lt;/p&gt;

&lt;h2&gt;
  
  
  Build and Switch Dev Containers
&lt;/h2&gt;

&lt;p&gt;In VS Code, open the root-level folder (&lt;code&gt;dev-container&lt;/code&gt; in my demo). Run &lt;code&gt;Dev Containers: Reopen in Container&lt;/code&gt; from the Command Palette and select the project to build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Feh2cmjjn1ed2mqn09oaz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Feh2cmjjn1ed2mqn09oaz.png" alt="choose dev container to build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This triggers the Dev Container build for the selected project.&lt;/p&gt;

&lt;p&gt;To switch between projects, use &lt;code&gt;Dev Containers: Switch Container&lt;/code&gt; from the Command Palette and select the desired project.&lt;/p&gt;

&lt;p&gt;After each Dev Container is built, we can use this &lt;code&gt;Switch Container&lt;/code&gt; command to switch between projects, the current VS Code window will reload each time and connect to the selected Dev Container.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo Preview
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://github.com/graezykev/dev-container/tree/part-5-shared-configure-for-multiple-projects" rel="noopener noreferrer"&gt;my demo&lt;/a&gt;, I have three projects: two &lt;code&gt;Node.js&lt;/code&gt; projects and one &lt;code&gt;Python&lt;/code&gt; project, each starts an HTTP server with a web page.&lt;/p&gt;

&lt;p&gt;When visited, the projects write "visiting records" to the shared database and display all records on the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fie2pwkv7xqql9cd8j83d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fie2pwkv7xqql9cd8j83d.png" alt="preview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And remember the port mapping we mentioned before?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxgeiswmomeji4ryl87p6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxgeiswmomeji4ryl87p6.png" alt="visiting records"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devcontainer</category>
    </item>
    <item>
      <title>Dev Containers - Part 4: Remote Dev - Develop on a Remote Docker Host</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 21 Jun 2024 06:46:04 +0000</pubDate>
      <link>https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l</link>
      <guid>https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l</guid>
      <description>&lt;p&gt;Welcome to the fourth guide in the Dev Container series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727"&gt;Dev Containers - Why You Need Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;Part 1: Quick Start - Basic Setup and Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Part 2: Image, Features, Workspace, Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Part 3: Full Stack Dev - Docker Compose &amp;amp; Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 4: Remote Dev - Develop on a Remote Docker Host&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi"&gt;Part 5: Multiple Projects &amp;amp; Shared Container Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started, you can clone &lt;a href="https://github.com/graezykev/dev-container/tree/part-4-remote-dev" rel="noopener noreferrer"&gt;my demo project&lt;/a&gt; using the following command:&lt;/p&gt;

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

git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-4-remote-dev https://github.com/graezykev/dev-container.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Here comes my favourite part: &lt;strong&gt;Remote Development&lt;/strong&gt;, or more fashionably, cloud development.&lt;/p&gt;

&lt;p&gt;In the previous parts, we focused on standalone PCs for building dev containers and development. In this part, I'll show you how to develop when you're not at the office or even without a working PC at your disposal.&lt;/p&gt;

&lt;p&gt;Here's how the VS Code team explains the remote development architecture:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frl19xjq5uvbo4cwpq3pf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frl19xjq5uvbo4cwpq3pf.png" alt="remote docker host"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the left, VS Code can be a &lt;strong&gt;native client&lt;/strong&gt; installed on your development machine (desktop, laptop, tablet, etc.) or a &lt;strong&gt;VS Code web&lt;/strong&gt; accessed via any web browser. This flexibility means you can work anywhere with an internet connection and a web browser.&lt;/p&gt;

&lt;p&gt;There are two primary ways of remote development:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Connect to remote and virtual machines with Visual Studio Code via SSH&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Connect to a remote machine via a secure tunnel, without configuring SSH&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'll guide you through the second method, which is simpler and avoids the lengthy, laborious tasks of installing and configuring SSH servers and clients, enabling us to work anywhere without a PC.&lt;/p&gt;

&lt;p&gt;I won't delve into the concepts of &lt;a href="https://code.visualstudio.com/docs/remote/vscode-server" rel="noopener noreferrer"&gt;&lt;strong&gt;Visual Studio Code Server&lt;/strong&gt;&lt;/a&gt;, although we'll be leveraging its capabilities.&lt;/p&gt;
&lt;h2&gt;
  
  
  Developing with Remote Tunnels
&lt;/h2&gt;
&lt;h3&gt;
  
  
  0. Prepare
&lt;/h3&gt;

&lt;p&gt;For remote development, you need at least two devices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Remote Machine&lt;/strong&gt;: This serves the code and dev environment. It can be your office desktop/laptop, a Virtual Machine, or a cloud machine (e.g., AWS EC2). In this guide, I'll use an Ubuntu Linux server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dev Machine&lt;/strong&gt;: This can be another desktop, laptop, iPad, Surface Tablet, Android Tablet, or even a mobile phone (although using a phone is only practical for urgent scenarios).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally, you'll need a &lt;strong&gt;GitHub/Microsoft account&lt;/strong&gt;. This account acts as a bridge to connect the dev machine to the remote machine via &lt;a href="https://code.visualstudio.com/docs/remote/vscode-server" rel="noopener noreferrer"&gt;&lt;strong&gt;Visual Studio Code Server&lt;/strong&gt;&lt;/a&gt;, ensuring secure access.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Install Docker (Remote Machine)
&lt;/h3&gt;

&lt;p&gt;Install Docker on the remote machine. You don't need it on the dev machine. Instructions for installing Docker are in &lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6#2-install-docker"&gt;Part 1&lt;/a&gt;. It's straightforward, especially on Mac/Windows PCs, and even on headless systems like an Ubuntu Linux server.&lt;/p&gt;

&lt;p&gt;Interestingly, &lt;strong&gt;you can skip installing Docker for now&lt;/strong&gt;, as you can install it at the end of step 4 (Connect to Remote Machine) using the terminal from the dev machine.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. Install Code CLI (Remote Machine)
&lt;/h3&gt;

&lt;p&gt;On the remote machine, &lt;strong&gt;download and uncompress&lt;/strong&gt; the VS Code CLI from &lt;a href="https://code.visualstudio.com/download" rel="noopener noreferrer"&gt;https://code.visualstudio.com/download&lt;/a&gt; based on your operating system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fy4tft2d3fe683f19qlh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fy4tft2d3fe683f19qlh2.png" alt="download vs code cli"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Ubuntu Linux, use these commands (replace &lt;code&gt;cli-alpine-x64&lt;/code&gt; with &lt;code&gt;cli-alpine-arm64&lt;/code&gt; or &lt;code&gt;cli-linux-armhf&lt;/code&gt; as needed):&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

curl &lt;span class="nt"&gt;-Lk&lt;/span&gt; &lt;span class="s1"&gt;'https://code.visualstudio.com/sha/download?build=stable&amp;amp;os=cli-alpine-x64'&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; vscode_cli.tar.gz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; vscode_cli.tar.gz


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also try my fully automatic shell script to install the CLI: &lt;a href="https://gist.github.com/graezykev/b7c981c4966d49e580cf1fddc0c52559" rel="noopener noreferrer"&gt;https://gist.github.com/graezykev/b7c981c4966d49e580cf1fddc0c52559&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After uncompressing, you'll find the executable binary file:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F89d53hr57kj214z37hya.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F89d53hr57kj214z37hya.png" alt="code exec bin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Create Secure Tunnel (Remote Machine)
&lt;/h3&gt;

&lt;p&gt;Run the binary file to create a secure tunnel:&lt;/p&gt;

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

./code tunnel &lt;span class="nt"&gt;--accept-server-license-terms&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Follow the steps to set up the tunnel:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fsh7gj4118owhsk543wbg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fsh7gj4118owhsk543wbg.png" alt="steps to create a secure tunnel"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use the GitHub/Microsoft account mentioned earlier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The CLI will output a &lt;strong&gt;vscode.dev URL&lt;/strong&gt; tied to this remote machine, such as &lt;code&gt;https://vscode.dev/tunnel/&amp;lt;machine_name&amp;gt;/&amp;lt;folder_name&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Connect to Remote Machine
&lt;/h3&gt;

&lt;p&gt;Now you can connect to the remote machine using the dev machine. You have two options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Directly visit the vscode.dev URL&lt;/strong&gt; from a web browser.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpncps0lumddmozvv4964.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpncps0lumddmozvv4964.gif" alt="visit vscode.dev URL on a web browser"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use a VS Code client&lt;/strong&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack" rel="noopener noreferrer"&gt;Remote Development&lt;/a&gt; extension pack (search &lt;code&gt;remote dev&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Use VS Code's &lt;code&gt;Command Palette&lt;/code&gt; and choose &lt;code&gt;Connect to Tunnel&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Login and verify with the GitHub/Microsoft account you used to create the tunnel.&lt;/li&gt;
&lt;li&gt;Choose the remote machine name created in the previous step.&lt;/li&gt;
&lt;li&gt;Wait for the connection and check the status.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;Here are the steps in action:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F668vj1h1vbypahebysf1.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F668vj1h1vbypahebysf1.gif" alt="visit vscode.dev URL on a VS Code client"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Either option you choose, when you try to connect for the first time, you'll be prompted to log into your Github/Microsoft account at a &lt;code&gt;https://github.com/login/oauth/authorize...&lt;/code&gt; URL.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once connected, you’ll see the remote machine's name (e.g., &lt;code&gt;my-remote-ubuntu&lt;/code&gt;) at the bottom left of VS Code. You now have terminal access to the remote machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fid35ljkakggqaytveu3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fid35ljkakggqaytveu3x.png" alt="remote machine and terminal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As mentioned in step 1, you can install Docker from this terminal. In the example below, Docker is installed from the VS Code client's terminal following this &lt;a href="https://docs.docker.com/engine/install/ubuntu/#installation-methods" rel="noopener noreferrer"&gt;guide&lt;/a&gt;. You can even do it from VS Code web.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F0pb8eyll4mxmd3kke8th.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F0pb8eyll4mxmd3kke8th.gif" alt="install docker in remote machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we’ve established a connection to the remote machine. In the next steps, we’ll clone the code and build a dev container on it to enjoy a unified development environment.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Clone Your Project
&lt;/h3&gt;

&lt;p&gt;Clone your project (which has a &lt;code&gt;.devcontainer&lt;/code&gt; folder and its configurations). You can use my project for testing:&lt;/p&gt;


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

&lt;p&gt;git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-4-remote-dev &lt;a href="https://github.com/graezykev/dev-container.git" rel="noopener noreferrer"&gt;https://github.com/graezykev/dev-container.git&lt;/a&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
&lt;br&gt;
  &lt;br&gt;
  

&lt;ol&gt;
&lt;li&gt;Build the Dev Container
&lt;/li&gt;
&lt;/ol&gt;
&lt;/h3&gt;


&lt;blockquote&gt;
&lt;p&gt;At the time of writing, building a dev container via &lt;strong&gt;VS Code web&lt;/strong&gt; is not yet supported. You need to do this using a &lt;strong&gt;native VS Code client&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The dev container isn’t built yet, so we're still unable to use the container environment. Follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use VS Code's &lt;code&gt;Open Folder&lt;/code&gt; to open the project.&lt;/li&gt;
&lt;li&gt;Use VS Code's &lt;code&gt;Command Palette&lt;/code&gt; and choose &lt;code&gt;Reopen in Container&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wait for the build to complete.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fcwsylere66hnr8qmc1cn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fcwsylere66hnr8qmc1cn.gif" alt="open folder and reopen in container and build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the container is built, enjoy the dev environment, ports mapping, VS Code extensions, settings, etc. Lifecycle commands in &lt;code&gt;devcontainer.json&lt;/code&gt; will also run in the container after it’s built.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fp10wqvqqdhtummpnqjny.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fp10wqvqqdhtummpnqjny.png" alt="after container built - port mapping &amp;amp; lifecycle scripts &amp;amp; extensions &amp;amp; settings"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Node.js program listens on port 8000 on the remote machine’s dev container, and the port is mapped directly to your dev machine, so you can visit it via &lt;code&gt;localhost:8000&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Moreover, the VS Code extensions specified in &lt;code&gt;devcontainer.json&lt;/code&gt; are installed on the remote machine’s dev container, not on your dev machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fnqh2mdcco7pe1cadcpir.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fnqh2mdcco7pe1cadcpir.png" alt="after container built - installed software"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, although the remote machine doesn’t have Node.js or Python installed, you can still enjoy the pre-installed software in the dev container built on your remote machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work Anywhere
&lt;/h2&gt;

&lt;p&gt;Now you’re able to work anywhere with internet access!&lt;/p&gt;

&lt;p&gt;Use a spare laptop (with Windows, Mac, or Linux OS) to install VS Code, login with the GitHub/Microsoft account, and connect to the remote machine via "&lt;strong&gt;Remote Explorer&lt;/strong&gt;" (Don't forget to install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.vscode-remote-extensionpack" rel="noopener noreferrer"&gt;Remote Development&lt;/a&gt; extension pack).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fscs51jhl5wd6a3fj3v1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fscs51jhl5wd6a3fj3v1y.png" alt="remote explorer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you don’t want to install anything, use a tablet device like an iPad to visit the &lt;strong&gt;vscode.dev URL&lt;/strong&gt; and start developing.&lt;/p&gt;

&lt;p&gt;In urgent scenarios, you can even use a mobile phone to visit the &lt;strong&gt;vscode.dev URL&lt;/strong&gt;, make code changes, or run commands on the remote machine.&lt;/p&gt;

</description>
      <category>devcontainer</category>
    </item>
    <item>
      <title>Dev Containers - Part 3: Full Stack Dev - Docker Compose &amp; Database</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 21 Jun 2024 06:45:51 +0000</pubDate>
      <link>https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o</link>
      <guid>https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o</guid>
      <description>&lt;p&gt;Welcome to the third guide in the Dev Container series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727"&gt;Dev Containers - Why You Need Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;Part 1: Quick Start - Basic Setup and Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Part 2: Image, Features, Workspace, Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 3: Full Stack Dev - Docker Compose &amp;amp; Database&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l"&gt;Part 4: Remote Dev - Develop on a Remote Docker Host&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi"&gt;Part 5: Multiple Projects &amp;amp; Shared Container Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started, you can clone &lt;a href="https://github.com/graezykev/dev-container/tree/part-3-use-docker-compose-and-db" rel="noopener noreferrer"&gt;my demo project&lt;/a&gt; using the following command:&lt;/p&gt;

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

git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-3-use-docker-compose-and-db https://github.com/graezykev/dev-container.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Alternatively, the faster approach is to use GitHub's Codespaces to run the demo (switch to branch &lt;code&gt;part-3-use-docker-compose-and-db&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fvvlgcksj6hb8pg52dihu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fvvlgcksj6hb8pg52dihu.png" alt="Run demo in Codespaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Imagine you're developing server applications relying on both &lt;code&gt;Node.js&lt;/code&gt; and &lt;code&gt;PostgreSQL&lt;/code&gt;. You might initially use installation commands in a &lt;code&gt;Dockerfile&lt;/code&gt; (as discussed in &lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;Part 1&lt;/a&gt;) or leverage Dev Container &lt;a href="https://containers.dev/features" rel="noopener noreferrer"&gt;Features&lt;/a&gt; (covered in &lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Part 2&lt;/a&gt;) to install PostgreSQL within your container.&lt;/p&gt;

&lt;p&gt;However, managing PostgreSQL this way can lead to unexpected behaviours if not handled correctly.&lt;/p&gt;

&lt;p&gt;For instance, if your database startup script exits unexpectedly, the container may stop unless it includes a command to keep running, such as starting a shell or a daemon process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Isolation
&lt;/h2&gt;

&lt;p&gt;A more robust solution for development involves using &lt;a href="https://docs.docker.com/compose/" rel="noopener noreferrer"&gt;Docker Compose&lt;/a&gt; to manage both your application container and your PostgreSQL service in &lt;strong&gt;separate&lt;/strong&gt; containers. It's generally better to handle services like databases with separate containers or services, especially in production environments.&lt;/p&gt;

&lt;p&gt;Using Docker Compose, you can manage two containers: one for development and the other for the database.&lt;/p&gt;

&lt;p&gt;Install only the PostgreSQL client on your development container to connect to the PostgreSQL server running in the other container.&lt;/p&gt;

&lt;h2&gt;
  
  
  I. Compose Configuration
&lt;/h2&gt;

&lt;h3&gt;
  
  
  0. docker-compose.yml
&lt;/h3&gt;

&lt;p&gt;Creating a Docker Compose configuration file &lt;code&gt;docker-compose.yml&lt;/code&gt; is crucial for using multiple containers:&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;touch&lt;/span&gt; .devcontainer/docker-compose.yml


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# docker-compose.yml&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker.io/your-user-name/your-image-name&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;..:/workspaces/dev-container:cached&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8001:8000&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_HOST=${POSTGRES_HOST}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=${POSTGRES_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${POSTGRES_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=${POSTGRES_DB}&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;

  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:latest&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=${POSTGRES_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${POSTGRES_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=${POSTGRES_DB}&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres-data:/var/lib/postgresql/data&lt;/span&gt;  &lt;span class="c1"&gt;# Named volume for persisting PostgreSQL data&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="c1"&gt;# Define a named volume for PostgreSQL data persistence&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's break down some basic concepts in this &lt;code&gt;docker-compose.yml&lt;/code&gt;. You can also jump straight to the the next step of Entry Point and come back to the explanations below.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Define &lt;code&gt;services&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We define 2 &lt;code&gt;services&lt;/code&gt; here, each as a container: &lt;code&gt;app&lt;/code&gt; and &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This setup allows extending the project to include multiple other services (containers) like &lt;code&gt;app2&lt;/code&gt;, &lt;code&gt;app3&lt;/code&gt;, &lt;code&gt;redis&lt;/code&gt;, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Specify &lt;code&gt;image&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Specify the image used in &lt;code&gt;devcontainer.json&lt;/code&gt; in this configuration file to initiate the &lt;code&gt;app&lt;/code&gt; container.&lt;/p&gt;

&lt;p&gt;For the database container, use the official image &lt;code&gt;postgres:latest&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Define &lt;code&gt;volumes&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Volumes are pretty similar to the workspace mounting mentioned in &lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o#workspace"&gt;Part 2&lt;/a&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;..:/workspaces/dev-container:cached&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres-data:/var/lib/postgresql/data&lt;/span&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If using Docker Compose, eliminate the &lt;code&gt;workspaceMount&lt;/code&gt; in &lt;code&gt;devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gd"&gt;- "workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind,consistency=cached",
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;For the &lt;code&gt;volumes&lt;/code&gt; under container &lt;code&gt;app&lt;/code&gt;, the value &lt;code&gt;..:/workspaces/dev-container&lt;/code&gt; is separated by a colon &lt;code&gt;:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;..&lt;/code&gt; before the colon represents the path on the host machine, while &lt;code&gt;/workspaces/dev-container&lt;/code&gt; after the colon represents the path in the container.&lt;/p&gt;

&lt;p&gt;The absolute path of the Docker Compose file is &lt;code&gt;/path/to/dev-container/.devcontainer/docker-compose.yml&lt;/code&gt;, so &lt;code&gt;..&lt;/code&gt; equals &lt;code&gt;/path/to/dev-container/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This setup mounts &lt;code&gt;/path/to/dev-container/&lt;/code&gt; on the host machine to &lt;code&gt;/workspaces/dev-container&lt;/code&gt; in the container.&lt;/p&gt;

&lt;p&gt;Changes in one are reflected in the other. Whatever you change &lt;code&gt;/path/to/dev-container/&lt;/code&gt; on the host machine, you're making the same change in the &lt;code&gt;/workspaces/dev-container&lt;/code&gt; of the container, and vice versa.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;volumes&lt;/code&gt; under container &lt;code&gt;postgres&lt;/code&gt;, the value is &lt;code&gt;postgres-data:/var/lib/postgresql/data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We don't have a path on the host machine to serve data for the database, so create a "virtual volume" via the configuration:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;postgres-data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;postgres-data&lt;/code&gt; is the name used in &lt;code&gt;postgres-data:/var/lib/postgresql/data&lt;/code&gt;, this volume is mounted to container &lt;code&gt;postgres&lt;/code&gt;'s path &lt;code&gt;/var/lib/postgresql/data&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Creating a virtual volume enables the persistence of database data and the ability to share it between multiple applications.&lt;/p&gt;

&lt;p&gt;For instance, if you delete the container &lt;code&gt;postgres&lt;/code&gt;, the data saved in the volume &lt;code&gt;postgres-data&lt;/code&gt; still exists and is reusable when creating another database with the same volume.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Environment variables
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;docker compose&lt;/code&gt; command will automatically pick up a file called &lt;code&gt;.env&lt;/code&gt; in the folder containing the &lt;code&gt;docker-compose.yml&lt;/code&gt;. That's why I commented out this section:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="nn"&gt;...&lt;/span&gt;
    &lt;span class="c1"&gt;# env_file:&lt;/span&gt;
    &lt;span class="c1"&gt;#   - .env # This is a default setting&lt;/span&gt;
    &lt;span class="c1"&gt;#   - ...  # Put other xxx.env files here&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Unless you use multiple &lt;code&gt;.env&lt;/code&gt; files, you don't need to specify any other files here.&lt;/p&gt;

&lt;p&gt;Also, we need to remove the &lt;code&gt;runArgs&lt;/code&gt; in &lt;code&gt;devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gd"&gt;- "runArgs": [
-   "--env-file",
-   "${localWorkspaceFolder}/.devcontainer/.env"
- ]
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Variables in &lt;code&gt;.env&lt;/code&gt; are not automatically injected into containers; pass them manually for each container:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_HOST=${POSTGRES_HOST}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=${POSTGRES_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${POSTGRES_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=${POSTGRES_DB}&lt;/span&gt;
  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=${POSTGRES_USER}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${POSTGRES_PASSWORD}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=${POSTGRES_DB}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;${POSTGRES_PASSWORD}&lt;/code&gt; represents the &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; defined in &lt;code&gt;.env&lt;/code&gt;, and so forth.&lt;/p&gt;

&lt;p&gt;Pass these variables to container &lt;code&gt;postgres&lt;/code&gt; to create a database with specific credentials, and to container &lt;code&gt;app&lt;/code&gt; to connect to the database in container &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: In real scenarios, &lt;code&gt;.env&lt;/code&gt; and database credentials should be handled by CI/CD systems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  5. Map &lt;code&gt;ports&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;We learned "Forwarding Ports" in &lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6#forwarding-ports"&gt;Part 1&lt;/a&gt; but this is a bit different.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ports&lt;/code&gt; section in &lt;code&gt;docker-compose.yml&lt;/code&gt; exposes container ports to the host machine, specified as &lt;code&gt;HOST:CONTAINER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;While the &lt;code&gt;forwardPorts&lt;/code&gt; section in &lt;code&gt;devcontainer.json&lt;/code&gt; is specific to VS Code Dev Containers and makes ports accessible within the container and to linked services.&lt;/p&gt;

&lt;p&gt;In scenarios with multiple containers (&lt;code&gt;app&lt;/code&gt;, &lt;code&gt;app1&lt;/code&gt;, &lt;code&gt;app2&lt;/code&gt;), each listening on port &lt;code&gt;8080&lt;/code&gt;, map the ports as follows:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8001:8080&lt;/span&gt;
  &lt;span class="na"&gt;app1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8002:8080&lt;/span&gt;
  &lt;span class="na"&gt;app2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;8003:8080&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This setup allows using port &lt;code&gt;8001&lt;/code&gt; on the host to access container &lt;code&gt;app&lt;/code&gt;'s port &lt;code&gt;8080&lt;/code&gt;, port &lt;code&gt;8002&lt;/code&gt; for container &lt;code&gt;app1&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flf7vhjik5z70xfzjnte7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flf7vhjik5z70xfzjnte7.png" alt="docker compose port mapping"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  II. Entry Point
&lt;/h2&gt;

&lt;p&gt;Update &lt;code&gt;devcontainer.json&lt;/code&gt; to use Docker Compose as the entry point:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  "name": "Dev Container",
&lt;span class="gd"&gt;- "image": "docker.io/your-user-name/your-image-name"
&lt;/span&gt;&lt;span class="gi"&gt;+ "dockerComposeFile": [
+   "docker-compose.yml"
+ ],
+ "service": "app",
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;app&lt;/code&gt; points to the service defined in &lt;code&gt;docker-compose.yml&lt;/code&gt;. The &lt;code&gt;depends_on&lt;/code&gt; in &lt;code&gt;docker-compose.yml&lt;/code&gt; ensures &lt;code&gt;postgres&lt;/code&gt; starts with &lt;code&gt;app&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  III. Full-Stack Development
&lt;/h2&gt;

&lt;p&gt;With everything prepared, build the Dev Containers (use VS Code's "Open in Container") and start full-stack development.&lt;/p&gt;

&lt;p&gt;In my demo, I have a &lt;code&gt;Node.js&lt;/code&gt; server (&lt;a href="https://github.com/graezykev/dev-container/blob/part-3-use-docker-compose-and-db/index.js" rel="noopener noreferrer"&gt;index.js&lt;/a&gt;). You can copy &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt; to your project for the demo.&lt;/p&gt;

&lt;p&gt;This program runs in container &lt;code&gt;app&lt;/code&gt;, connects to the database in container &lt;code&gt;postgres&lt;/code&gt; with credentials from &lt;code&gt;.env&lt;/code&gt;, creates a table named &lt;code&gt;clients&lt;/code&gt; (on first run), writes new data, and displays all data on the web page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frryk64uef1cortywi60m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frryk64uef1cortywi60m.png" alt="docker compose environment variables"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Flf7vhjik5z70xfzjnte7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Flf7vhjik5z70xfzjnte7.png" alt="docker compose port mapping"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  VI. Add Database Client (optional)
&lt;/h2&gt;

&lt;p&gt;To use the &lt;code&gt;psql&lt;/code&gt; command line in container &lt;code&gt;app&lt;/code&gt; to connect to the PostgreSQL server in container &lt;code&gt;postgres&lt;/code&gt;, install the PostgreSQL client via Dev Container Features in &lt;code&gt;devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"features"&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;"ghcr.io/robbert229/devcontainer-features/postgresql-client:1"&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;Then, in container &lt;code&gt;app&lt;/code&gt;'s terminal, connect to the database:&lt;/p&gt;

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

psql &lt;span class="nt"&gt;-h&lt;/span&gt; postgres &lt;span class="nt"&gt;-U&lt;/span&gt; postgres &lt;span class="nt"&gt;-d&lt;/span&gt; postgres
&lt;span class="c"&gt;# or&lt;/span&gt;
&lt;span class="c"&gt;# psql -h $POSTGRES_HOST -U $POSTGRES_USER -d $POSTGRES_DB&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fn3mfzodkqo2kqdta4d4l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fn3mfzodkqo2kqdta4d4l.png" alt="connect to database from app container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l"&gt;next guide&lt;/a&gt; will focus on developing remotely by putting Dev Containers on a remote machine (cloud machine). This allows you to use the VS Code client or web to connect to the remote machine and Dev Containers, enabling development from anywhere.&lt;/p&gt;

</description>
      <category>devcontainer</category>
    </item>
    <item>
      <title>Dev Containers - Part 2: Image, Features, Workspace, Environment Variables</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 21 Jun 2024 06:45:39 +0000</pubDate>
      <link>https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o</link>
      <guid>https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o</guid>
      <description>&lt;p&gt;Welcome to the second guide in the Dev Container series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727"&gt;Dev Containers - Why You Need Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;Part 1: Quick Start - Basic Setup and Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 2: Image, Features, Workspace, Environment Variables&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Part 3: Full Stack Dev - Docker Compose &amp;amp; Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l"&gt;Part 4: Remote Dev - Develop on a Remote Docker Host&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi"&gt;Part 5: Multiple Projects &amp;amp; Shared Container Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started, clone &lt;a href="https://github.com/graezykev/dev-container/tree/part-2-use-image-and-features" rel="noopener noreferrer"&gt;my demo project&lt;/a&gt; using the following command:&lt;/p&gt;

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

git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-2-use-image-and-features https://github.com/graezykev/dev-container.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Alternatively, the faster approach is to use GitHub's Codespaces to run the demo (switch to branch &lt;code&gt;part-2-use-image-and-features&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ffespd1dhclzphenlqv8u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ffespd1dhclzphenlqv8u.png" alt="Run demo in Codespaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In our &lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;last guide&lt;/a&gt;, we introduced the basics of using &lt;code&gt;.devcontainer&lt;/code&gt;, &lt;code&gt;devcontainer.json&lt;/code&gt;, and a &lt;code&gt;Dockerfile&lt;/code&gt; to configure and build a Dev Container.&lt;/p&gt;

&lt;p&gt;In this guide, we'll optimise the process by adding reusable images and extra software.&lt;/p&gt;

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

&lt;p&gt;Using a &lt;code&gt;Dockerfile&lt;/code&gt; in our &lt;code&gt;.devcontainer&lt;/code&gt; folder to build a Dev Container can be time-consuming, especially when pre-installing multiple software.&lt;/p&gt;

&lt;p&gt;Newcomers might face issues due to the time taken to install software while building via a &lt;code&gt;Dockerfile&lt;/code&gt;. They might also end up with different versions of the software, leading to inconsistencies.&lt;/p&gt;

&lt;p&gt;For example, the command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;might install different versions of &lt;code&gt;Node.js&lt;/code&gt; over time, leading to unexpected issues.&lt;/p&gt;

&lt;p&gt;To address this, we can provide a fixed environment with specific software versions, like &lt;code&gt;Go lang v1.22.4&lt;/code&gt;, and prevent unintended upgrades. We can pre-install multiple versions of &lt;code&gt;Node.js&lt;/code&gt; in this environment, allowing new team members to switch between versions easily.&lt;/p&gt;

&lt;p&gt;This fixed environment is known as a "Docker image" or simply "image."&lt;/p&gt;

&lt;p&gt;Newcomers just need to "clone" (download) this image and run it, similar to starting an operating system, but much faster.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use an Image
&lt;/h2&gt;

&lt;p&gt;To streamline the process for newcomers, we build a Docker image from a basic &lt;code&gt;Dockerfile&lt;/code&gt; and push it to a "cloud warehouse" - &lt;a href="https://hub.docker.com/" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Docker Hub serves as a free file server for our built, ready-to-use Docker images, which anyone can download and reuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Build &amp;amp; Push Image
&lt;/h3&gt;

&lt;p&gt;Building and pushing a Docker image is straightforward if you have a &lt;code&gt;Dockerfile&lt;/code&gt; from our &lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;last guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You will also need to sign up for a free account on Docker Hub.&lt;/p&gt;

&lt;p&gt;For detailed steps, check out this &lt;a href="https://github.com/graezykev/docker-build-push-101" rel="noopener noreferrer"&gt;guide to register, build, and push Docker images&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the end of this step you should get a URL of the image from Docker Hub like &lt;code&gt;docker.io/your-user-name/your-image-name&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Configure Image
&lt;/h3&gt;

&lt;p&gt;Specify this image in &lt;code&gt;devcontainer.json&lt;/code&gt; and remove the &lt;code&gt;Dockerfile&lt;/code&gt; reference:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gd"&gt;- "build": {
-   "dockerfile": "Dockerfile"
- }
&lt;/span&gt;&lt;span class="gi"&gt;+ "image": "docker.io/your-user-name/your-image-name"
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;You can check out my demo and copy the image I've built here: &lt;a href="https://github.com/graezykev/dev-container/blob/part-2-use-image-and-features/.devcontainer/devcontainer.json#L3" rel="noopener noreferrer"&gt;https://github.com/graezykev/dev-container/blob/part-2-use-image-and-features/.devcontainer/devcontainer.json#L3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;"image": "your-user-name/your-image-name"&lt;/code&gt; also works.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's all! When new teammates use "Open in Container" in VS Code to open our project, the image will be automatically downloaded, cloned, and started as a Dev Container on their machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;p&gt;Using an image to create Dev Containers is much faster, especially with a hefty &lt;code&gt;Dockerfile&lt;/code&gt;, as we only need to build the image once (or rebuild it if modified).&lt;/p&gt;

&lt;p&gt;However, it's yet to be ideal, consider scenarios with multiple tech stacks:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
├── project-go-lang
├── project-node-js-1
├── project-node-js-2
├── project-postgresql
├── project-python
└── project-rust


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Are you going to write a &lt;code&gt;Dockerfile&lt;/code&gt; to pre-install all tech stacks (&lt;code&gt;Node.js&lt;/code&gt;, &lt;code&gt;Go&lt;/code&gt;, &lt;code&gt;Python&lt;/code&gt;, etc.) and build an enormous image to use in each project?&lt;/p&gt;

&lt;p&gt;No! You don't do this.&lt;/p&gt;

&lt;p&gt;Instead, pre-install and pre-configure the most commonly used software in the image.&lt;/p&gt;

&lt;p&gt;For projects with divergent tech stacks, install extra software as needed.&lt;/p&gt;

&lt;p&gt;Adding extra software to a Dev Container is simple by specifying them in &lt;code&gt;devcontainer.json&lt;/code&gt; using the &lt;code&gt;features&lt;/code&gt; field. This allows different projects to use the same image with additional specific features.&lt;/p&gt;

&lt;p&gt;For example, add the &lt;code&gt;Python&lt;/code&gt; engine to &lt;code&gt;devcontainer.json&lt;/code&gt; in Project A:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  "image": "docker.io/your-user-name/your-image-name",
&lt;span class="gi"&gt;+ "features": {
+   "ghcr.io/devcontainers/features/python:1": {
+     "version": "latest"
+   }
+ }
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And add the &lt;code&gt;Go&lt;/code&gt; language engine to &lt;code&gt;devcontainer.json&lt;/code&gt; in Project B:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  "image": "docker.io/your-user-name/your-image-name",
&lt;span class="gi"&gt;+ "features": {
+   "ghcr.io/devcontainers/features/go:1": {}
+ }
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Find available Dev Container features here: &lt;a href="https://containers.dev/features" rel="noopener noreferrer"&gt;https://containers.dev/features&lt;/a&gt;. You can also &lt;a href="https://github.com/devcontainers/feature-starter" rel="noopener noreferrer"&gt;create your own feature&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Adding features is like adding sprinkles to your ice cream!&lt;/p&gt;

&lt;h2&gt;
  
  
  Workspace
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;devcontainer.json&lt;/code&gt; of &lt;a href="https://github.com/graezykev/dev-container/blob/part-2-use-image-and-features/.devcontainer/devcontainer.json#L13-L14" rel="noopener noreferrer"&gt;my demo&lt;/a&gt;, you'll see these lines:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"workspaceMount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source=${localWorkspaceFolder},target=/workspaces/${localWorkspaceFolderBasename},type=bind,consistency=cached"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/${localWorkspaceFolderBasename}"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These configurations aren't necessary for most cases but are worth explaining.&lt;/p&gt;

&lt;p&gt;Well, &lt;code&gt;workspaceMount&lt;/code&gt; and &lt;code&gt;workspaceFolder&lt;/code&gt; always come together, to understand them, let me explain the variables of &lt;code&gt;${localWorkspaceFolder}&lt;/code&gt; and &lt;code&gt;${localWorkspaceFolderBasename}&lt;/code&gt; first.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-defined Variables
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;${localWorkspaceFolder}&lt;/code&gt; and &lt;code&gt;${localWorkspaceFolderBasename}&lt;/code&gt; are some &lt;strong&gt;pre-defined&lt;/strong&gt; variables in &lt;code&gt;devcontainer.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you have a project &lt;code&gt;project-a&lt;/code&gt; on your host machine:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

.
└── path
    └── to
        ├── project-a
        │   └── .devcontainer
        │       ├── Dockerfile
        │       └── devcontainer.json
        ├── project-b
        └── project-c


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;${localWorkspaceFolder}&lt;/code&gt; represents the absolute path &lt;code&gt;/path/to/project-a&lt;/code&gt;, and &lt;code&gt;${localWorkspaceFolderBasename}&lt;/code&gt; represents the folder name &lt;code&gt;project-a&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  workspaceMount &amp;amp; workspaceFolder
&lt;/h3&gt;

&lt;p&gt;When you set:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"workspaceMount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source=/path/to/project-a,target=/workspaces/project-a, ..."&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;it means you're mounting the folder &lt;code&gt;/path/to/project-a&lt;/code&gt; from the host machine to the folder &lt;code&gt;/workspaces/project-a&lt;/code&gt; in the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Whatever you change &lt;code&gt;/path/to/project-a&lt;/code&gt; on the host machine, you're making the same change in the &lt;code&gt;/workspaces/project-a&lt;/code&gt; of the container, and vice versa&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;workspaceFolder&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/project-a"&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;sets the default source code location in VS Code within the container.&lt;/p&gt;

&lt;p&gt;i.e., &lt;strong&gt;when we connect to the container in VS Code, the default source code location will be set to &lt;code&gt;workspaceFolder&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And, the &lt;code&gt;postStartCommand&lt;/code&gt; (we mentioned in our &lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;last guide&lt;/a&gt;) is also run within &lt;code&gt;workspaceFolder&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to Use
&lt;/h3&gt;

&lt;p&gt;You generally don't need to set &lt;code&gt;workspaceMount&lt;/code&gt; and &lt;code&gt;workspaceFolder&lt;/code&gt;. However, in scenarios where &lt;code&gt;project-a&lt;/code&gt; needs to reference code in &lt;code&gt;project-b&lt;/code&gt; or &lt;code&gt;project-c&lt;/code&gt; without modifying them, you can set:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"workspaceMount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"source=/path/to,target=/workspaces, ..."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"workspaceFolder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/workspaces/project-a"&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This mounts all projects under &lt;code&gt;/path/to&lt;/code&gt; into the container while only modifying &lt;code&gt;project-a&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Environment Variables
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;devcontainer.json&lt;/code&gt; of &lt;a href="https://github.com/graezykev/dev-container/blob/part-2-use-image-and-features/.devcontainer/devcontainer.json#L9-L12" rel="noopener noreferrer"&gt;my demo&lt;/a&gt;, I have:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"runArgs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"--env-file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"${localWorkspaceFolder}/.devcontainer/.env"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Environment variables in Dev Containers are useful for managing configuration settings specific to your application or development environment, such as database connection strings, API keys, or feature flags.&lt;/p&gt;

&lt;p&gt;By using environment variables, you can securely store sensitive information without hardcoding them in your code.&lt;/p&gt;

&lt;p&gt;Moreover, creating separate environment variable files for development, testing, and production environments are very common use case.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to Set Environment Variables
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;.env&lt;/code&gt; file with &lt;strong&gt;key/value&lt;/strong&gt; pairs (mostly handled by CI/CD systems), and reference it via &lt;code&gt;runArgs&lt;/code&gt; in &lt;code&gt;devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nl"&gt;"runArgs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"--env-file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"${localWorkspaceFolder}/.devcontainer/.env"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These variables will be injected into the container as system variables:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmi0t2mjrgl82xjlieaed.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmi0t2mjrgl82xjlieaed.png" alt="use arguments, variables in dev container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the container is built and running, the variables can be used inside the container via Linux commands, shell scripts, or in your code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next
&lt;/h2&gt;

&lt;p&gt;In our next guide &lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Full Stack Dev - Docker Compose &amp;amp; Database&lt;/a&gt;, we'll use environment variables to manage database passwords and usernames, avoiding exposure in version control.&lt;/p&gt;

</description>
      <category>devcontainer</category>
    </item>
    <item>
      <title>Dev Containers - Part 1: Quick Start - Basic Setup and Usage</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 21 Jun 2024 06:45:23 +0000</pubDate>
      <link>https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6</link>
      <guid>https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6</guid>
      <description>&lt;p&gt;Welcome to the first guide in the Dev Container series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727"&gt;Dev Containers - Why You Need Them&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Part 1: Quick Start - Basic Setup and Usage&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Part 2: Image, Features, Workspace, Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Part 3: Full Stack Dev - Docker Compose &amp;amp; Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l"&gt;Part 4: Remote Dev - Develop on a Remote Docker Host&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi"&gt;Part 5: Multiple Projects &amp;amp; Shared Container Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To get started, you can clone &lt;a href="https://github.com/graezykev/dev-container/tree/part-1-use-docker-file" rel="noopener noreferrer"&gt;my demo project&lt;/a&gt; using the following command:&lt;/p&gt;

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

git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-1-use-docker-file https://github.com/graezykev/dev-container.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Alternatively, the faster approach is to use GitHub's Codespaces to run the demo (switch to branch &lt;code&gt;part-1-use-docker-file&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fmj26epqgl662vyoddxma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fmj26epqgl662vyoddxma.png" alt="Run demo in Codespaces"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In my previous post, I explained &lt;a href="https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727"&gt;why Dev Containers are essential&lt;/a&gt;. Now, let's dive into setting up a simple Dev Container.&lt;/p&gt;

&lt;p&gt;Imagine we have a &lt;code&gt;Node.js&lt;/code&gt; project (or &lt;code&gt;Python&lt;/code&gt;, &lt;code&gt;Go&lt;/code&gt; etc.).&lt;/p&gt;

&lt;p&gt;We want anyone joining our project to start developing without manually installing &lt;code&gt;Node.js&lt;/code&gt;, or other required software (&lt;code&gt;YARN&lt;/code&gt;, &lt;code&gt;PNPM&lt;/code&gt;, etc.).&lt;/p&gt;

&lt;p&gt;We also want to ensure they have the necessary VS Code extensions and adhere to our coding conventions, like using 2 spaces for indentation.&lt;/p&gt;

&lt;p&gt;By defining a Dev Container, we can share all these setups seamlessly.&lt;/p&gt;

&lt;p&gt;Future teammates will only need to build the Dev Container and start coding, without the hassle of setting up environments and installing software from scratch.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Install VS Code
&lt;/h2&gt;

&lt;p&gt;First, install the VS Code client on your computer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: &lt;a href="https://www.jetbrains.com/help/webstorm/connect-to-devcontainer.html" rel="noopener noreferrer"&gt;JetBrains WebStorm&lt;/a&gt; also supports Dev Containers, though I haven't tried it yet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  2. Install Docker
&lt;/h2&gt;

&lt;p&gt;Installing Docker is straightforward. Follow the &lt;a href="https://docs.docker.com/engine/install/" rel="noopener noreferrer"&gt;official guide&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Windows PC&lt;/strong&gt;: Install &lt;strong&gt;Docker Desktop&lt;/strong&gt; with a few clicks. &lt;a href="https://docs.docker.com/desktop/install/windows-install/" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mac&lt;/strong&gt;: Install &lt;strong&gt;Docker Desktop&lt;/strong&gt; with a few clicks. &lt;a href="https://docs.docker.com/desktop/install/mac-install/" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linux PC&lt;/strong&gt;: Install &lt;strong&gt;Docker Engine&lt;/strong&gt; with a few commands. &lt;a href="https://docs.docker.com/engine/install/ubuntu/" rel="noopener noreferrer"&gt;Installation guide&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, on Ubuntu Linux, use these commands:&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;# Add Docker's official GPG key:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl
&lt;span class="nb"&gt;sudo install&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; 0755 &lt;span class="nt"&gt;-d&lt;/span&gt; /etc/apt/keyrings
&lt;span class="nb"&gt;sudo &lt;/span&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.asc
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;a+r /etc/apt/keyrings/docker.asc

&lt;span class="c"&gt;# Add the repository to Apt sources:&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; /etc/os-release &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$VERSION_CODENAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update


&lt;/code&gt;&lt;/pre&gt;

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

&lt;span class="c"&gt;# Install Docker Engine:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin


&lt;/code&gt;&lt;/pre&gt;

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

&lt;span class="c"&gt;# Add user to the Docker group:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt; &lt;span class="c"&gt;# replace $USER with your Linux login username&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

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

&lt;span class="c"&gt;# Restart the system:&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;reboot


&lt;/code&gt;&lt;/pre&gt;

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

&lt;span class="c"&gt;# Verify the installation:&lt;/span&gt;
docker &lt;span class="nt"&gt;-v&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here's a recording of the installation process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F9yb82lwmm9o9hgx7gnvv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F9yb82lwmm9o9hgx7gnvv.gif" alt="ubuntu install docker"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Clone my demo project here: &lt;a href="https://github.com/graezykev/dev-container/tree/part-1-use-docker-file" rel="noopener noreferrer"&gt;https://github.com/graezykev/dev-container/tree/part-1-use-docker-file&lt;/a&gt;&lt;/p&gt;

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

git clone &lt;span class="nt"&gt;-b&lt;/span&gt; part-1-use-docker-file https://github.com/graezykev/dev-container.git


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;My demo project above might be sort of hefty and take a relatively long time to build. If you prefer a simpler demo, follow these steps to set up a "Hello World" project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a project folder and necessary files:&lt;/li&gt;
&lt;/ul&gt;

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

&lt;span class="nb"&gt;mkdir &lt;/span&gt;your-project-folder &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;your-project-folder &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; .devcontainer &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; .devcontainer/devcontainer.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; .devcontainer/Dockerfile &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;index.js


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Modify &lt;code&gt;index.js&lt;/code&gt; to create a "Hello World" web page:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&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;hostname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&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;port1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;200&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;setHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text/plain&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&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;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello, World Node.js!&lt;/span&gt;&lt;span class="se"&gt;\n&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="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hostname&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="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="s2"&gt;`Server running at http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;port1&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="p"&gt;})&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Create a simple &lt;code&gt;Dockerfile&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:24.04&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get upgrade &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; nodejs
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; npm


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Reference the &lt;code&gt;Dockerfile&lt;/code&gt; in &lt;code&gt;devcontainer.json&lt;/code&gt;:&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dev Container"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&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;"dockerfile"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Dockerfile"&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;This basic setup defines a Dev Container, creating a virtual Ubuntu system with &lt;code&gt;Node.js&lt;/code&gt; installed.&lt;/p&gt;

&lt;p&gt;To enhance the workflow, add an auto-run command in &lt;code&gt;devcontainer.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;{
  "name": "Dev Container",
  "build": {
    "dockerfile": "Dockerfile"
  },
&lt;span class="gi"&gt;+ "postStartCommand": "node index.js",
+ "forwardPorts": [8080]
&lt;/span&gt;}
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This command will run the &lt;code&gt;Node.js&lt;/code&gt; program every time the container starts.&lt;/p&gt;

&lt;p&gt;Additionally, add configurations for VS Code extensions and editor settings:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"customizations"&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;"vscode"&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;"settings"&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;"editor.tabSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"editor.insertSpaces"&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;"editor.detectIndentation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="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;"extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumer.vscode-eslint@3.0.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"esbenp.prettier-vscode"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"eamodio.gitlens"&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;I specify some useful VS Code extensions here, like &lt;code&gt;ESLint&lt;/code&gt;, &lt;code&gt;Prettier&lt;/code&gt; and the &lt;code&gt;GitLens&lt;/code&gt; visualising tools, in your scenario you may add your extensions. You can even specify the version of an extension such as &lt;code&gt;@3.0.5&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Build Dev Container
&lt;/h2&gt;

&lt;p&gt;Configuration works are done now, the next step is to build the Dev Container based on what we have configured.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Open Project in VS Code
&lt;/h3&gt;

&lt;p&gt;In VS Code, go to &lt;code&gt;File&lt;/code&gt; -&amp;gt; &lt;code&gt;Open Folder...&lt;/code&gt; and select your project folder (&lt;code&gt;dev-container&lt;/code&gt; for my demo project or &lt;code&gt;your-project-folder&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 Install Dev Containers Extension
&lt;/h3&gt;

&lt;p&gt;VS Code will prompt you to install the Dev Containers extension (&lt;code&gt;ms-vscode-remote.remote-containers&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fkw5jgkuzkyuyu0fz234v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fkw5jgkuzkyuyu0fz234v.png" alt="dev container installation prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click "Install" and wait for the installation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.3 Reopen in Container
&lt;/h3&gt;

&lt;p&gt;After installing the extension, VS Code will prompt you to "Reopen" your project in a container.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ft6e1q1gb3v8qhrcajxty.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ft6e1q1gb3v8qhrcajxty.png" alt="reopen in container prompt"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Reopen in Container&lt;/code&gt; to start the building process.&lt;/p&gt;

&lt;p&gt;Here is a recording of the process:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F2dqsyc59k0e8fgibepwk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F2dqsyc59k0e8fgibepwk.gif" alt="dev container installation and build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 Dev Container Built
&lt;/h3&gt;

&lt;p&gt;Once you reopen the project in a container, the building process will begin. This may take some time, depending on your Dockerfile and system performance. Subsequent starts will be faster unless you change the &lt;code&gt;.devcontainer&lt;/code&gt; setup.&lt;/p&gt;

&lt;p&gt;After building, enjoy a unified development environment with all applications, extensions, and settings pre-configured!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Moving forward, I'll refer to the computer you're using as the "&lt;strong&gt;host machine&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;VS Code and Docker run on the host machine, while the Dev Container is built and executed within Docker.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When the Dev Container is built or started, the &lt;code&gt;postStartCommand&lt;/code&gt; specified in &lt;code&gt;devcontainer.json&lt;/code&gt; will run, starting the &lt;code&gt;Node.js&lt;/code&gt; server:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fk7q6cgxiszrftrlkmmlu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fk7q6cgxiszrftrlkmmlu.png" alt="server started by postStartCommand"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;Node.js&lt;/code&gt; engine runs inside the Dev Container, not on your host machine. You can verify this from the terminal:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpr1io0j9fiunf3gzcnaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpr1io0j9fiunf3gzcnaf.png" alt="nodejs installed inside dev container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, the VS Code extensions specified in &lt;code&gt;devcontainer.json&lt;/code&gt; are installed inside the Dev Container, not on your host machine:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fdmwn31vpsj08ifi59tc5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fdmwn31vpsj08ifi59tc5.png" alt="vs code extensions installed in dev container"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Unified Working Environment
&lt;/h2&gt;

&lt;p&gt;We've now created a shareable working environment with a &lt;code&gt;Node.js&lt;/code&gt; engine, useful VS Code extensions, and consistent editor settings, all scoped to your project.&lt;/p&gt;

&lt;p&gt;Next, we'll optimise this Dev Container using a reusable Docker image and add extra software without modifying the &lt;code&gt;Dockerfile&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Dev Containers - Part 2: Image, Features, Workspace, Environment Variables&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix 1: Explanation of Basic Configurations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Building Entry Point
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;build&lt;/code&gt; -&amp;gt; &lt;code&gt;dockerfile&lt;/code&gt; field in &lt;code&gt;devcontainer.json&lt;/code&gt; serves as the entry point for the Dev Container. VS Code uses this to build and run the Docker image.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forwarding Ports
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;"forwardPorts": [8080]&lt;/code&gt; configuration forwards ports from the container to the host machine, allowing you to access the &lt;code&gt;Node.js&lt;/code&gt; server running inside the container from your host machine's browser.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;"forwardPorts": [8080]&lt;/code&gt; is an abbreviation for &lt;code&gt;"forwardPorts": ["8080:8080"]&lt;/code&gt;, you can make your own adjustment like &lt;code&gt;"forwardPorts": ["port-on-host:port-in-container"]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For more on &lt;code&gt;forwardPorts&lt;/code&gt;, refer to &lt;a href="https://containers.dev/implementors/json_reference/#general-properties" rel="noopener noreferrer"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix 2: Lifecycle Commands Explanation
&lt;/h2&gt;

&lt;p&gt;Lifecycle scripts in &lt;code&gt;devcontainer.json&lt;/code&gt; run at different points in the container’s lifecycle. These can be command lines or shell scripts. For detailed information, refer to &lt;a href="https://containers.dev/implementors/json_reference/#lifecycle-scripts" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;postCreateCommand&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you create a new Codespace, the postCreateCommand will run right after the container is set up.&lt;/li&gt;
&lt;li&gt;When you first open a project in a VS Code dev container, the postCreateCommand will run after the container is built or rebuilt.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If you want to install global npm packages or set environment variables, you should use this command.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;postStartCommand&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are working on a project in a Codespace. You stop the Codespace at the end of the day. The next day, you start the Codespace again to continue your work.&lt;/li&gt;
&lt;li&gt;You are developing an application in a VS Code dev container. You close VS Code or restart your computer, which stops the container. Later, you reopen VS Code and the container starts again.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;code&gt;postAttachCommand&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are using a Codespace for your project, and you disconnect from it (e.g., by closing the browser tab or your laptop going to sleep). Later, you reconnect to the same Codespace.&lt;/li&gt;
&lt;li&gt;You are working on a project in a VS Code dev container. You close VS Code or restart your computer, then later reopen VS Code and attach to the same running container.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>devcontainer</category>
    </item>
    <item>
      <title>Dev Containers - Part 0: Why You Need Them</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 21 Jun 2024 06:45:04 +0000</pubDate>
      <link>https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727</link>
      <guid>https://dev.to/graezykev/dev-containers-part-0-why-you-need-them-4727</guid>
      <description>&lt;h2&gt;
  
  
  It Works on My Machine
&lt;/h2&gt;

&lt;p&gt;Picture this: You're a newbie developer, excited to dive into your first big project. You clone the repository, open your terminal, and confidently type in the command to start the project. But instead of seeing the application running smoothly, you're greeted with a wall of error messages.&lt;/p&gt;

&lt;p&gt;Frustrated, you turn to your mentor for help.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt;: Hi mentor, I can't start this project! I'm encountering too many errors!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mentor&lt;/strong&gt;: Hmm, it works on my machine. Let me check. Oh, I see, I'm using &lt;code&gt;Node.js&lt;/code&gt; v14. What about you?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt;: I'm on v18.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mentor&lt;/strong&gt;: That explains it. Switch back to v14. It's easy, just run &lt;code&gt;nvm use 14&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt;: Sorry, what's NVM?&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You realise that just one small version difference can cause big headaches, and you're left wondering how to manage these differences seamlessly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Managing Software and Versions
&lt;/h2&gt;

&lt;p&gt;Our team has several full-stack JavaScript projects, some relying on different versions of &lt;code&gt;Node.js&lt;/code&gt;. Upgrading them all at once isn't feasible (it's annoying but common in real-world scenarios).&lt;/p&gt;

&lt;p&gt;Whenever a new member joins, they must install &lt;code&gt;NVM&lt;/code&gt; and multiple versions of &lt;code&gt;Node.js&lt;/code&gt; in order to run those projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  VS Code Extensions
&lt;/h2&gt;

&lt;p&gt;VS Code extensions can significantly boost development efficiency.&lt;/p&gt;

&lt;p&gt;For instance, many JavaScript developers use &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;&lt;code&gt;ESLint&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;&lt;code&gt;Prettier&lt;/code&gt;&lt;/a&gt; extensions to auto-format code and fix conventions with a simple &lt;code&gt;Command + S&lt;/code&gt; (&lt;code&gt;Control + S&lt;/code&gt;):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fza2ef53zdgz4i7tbetbd.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fza2ef53zdgz4i7tbetbd.gif" alt="vscode auto format"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As an experienced developer, you likely have other VS Code extensions to recommend to your team. However, getting them to install each one, and possibly specific versions of the extensions, can be challenging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Editor Settings
&lt;/h2&gt;

&lt;p&gt;Every team follows its own code conventions, such as using 2 or 4 &lt;strong&gt;spaces&lt;/strong&gt; for indentation or preferring &lt;strong&gt;tabs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;To reduce the hassle for your team, consider how difficult it would be if your new colleague's editor auto-inserts 4 spaces instead of the team's "2 spaces" convention when hitting &lt;code&gt;Tab&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up a Working Environment is Painful
&lt;/h2&gt;

&lt;p&gt;New team members often spend over two days installing and configuring necessary tools.&lt;/p&gt;

&lt;p&gt;Network issues, firewalls, dependency, and dependencies of a dependency can be a nightmare, especially in some scenarios they need to run Unix-like commands on a Windows PC.&lt;/p&gt;

&lt;h2&gt;
  
  
  Easier and Faster Onboarding
&lt;/h2&gt;

&lt;p&gt;All these problems can be resolved with Dev Containers.&lt;/p&gt;

&lt;p&gt;Here's how a newcomer can quickly integrate into the development team:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use any preferred machine or system (Mac, Windows, Ubuntu).&lt;/li&gt;
&lt;li&gt;Install Docker (without needing to understand it), which is straightforward.&lt;/li&gt;
&lt;li&gt;Install VS Code (just 2 to 3 clicks).&lt;/li&gt;
&lt;li&gt;Download or clone the project.&lt;/li&gt;
&lt;li&gt;Open the project in VS Code, follow the prompt to build a Dev Container, and start developing!&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Emergency Situations
&lt;/h2&gt;

&lt;p&gt;Imagine this: It's a sunny Saturday, and you're finally enjoying a much-needed day off. You're at the park with your family, soaking in the sun, when suddenly, your phone rings. It's your manager, and she sounds frantic.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manager&lt;/strong&gt;: We've got a huge problem! The code you pushed yesterday is causing memory leaks, and users are flooding us with complaints. We need a fix immediately!&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt;: I understand, but I'm not at home. I don't have my laptop with me.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manager&lt;/strong&gt;: This issue requires an urgent fix. Can you handle it?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Panic sets in. Fixing this issue requires just a tiny change, but how can you possibly manage it without your usual setup?&lt;/p&gt;

&lt;p&gt;With Dev Containers, you could calmly say:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You&lt;/strong&gt;: No problem. I can handle it right here.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You pull out your wife's iPad, connect to the internet, and open VS Code in the browser. Thanks to Dev Containers, you have an exact replica of your development environment, complete with all necessary tools and settings. Within minutes, you've made the fix and pushed the changes, all from a device you never thought you could code on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Dev Container?
&lt;/h2&gt;

&lt;p&gt;What exactly is a Dev Container, and how does it work?&lt;/p&gt;

&lt;p&gt;If you're familiar with backend development or containers like Docker, Dev Containers will seem familiar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fpmprshfhpwajd2vw52k3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fpmprshfhpwajd2vw52k3.png" alt="dev container architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even if you're new to containers, using Dev Containers in VS Code is simple and doesn't require in-depth knowledge of containers or Docker (though you need to install Docker).&lt;/p&gt;

&lt;p&gt;A Dev Container runs a Docker container with all development software, configurations, VS Code extensions, and settings, sharing environments with the local OS via VS Code.&lt;/p&gt;

&lt;p&gt;This creates reproducible development environments usable on any PC, anytime, anywhere.&lt;/p&gt;

&lt;p&gt;Dev Containers offer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Pre-installed and pre-configured systems and software:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latest stable LTS &lt;code&gt;Ubuntu&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tools like &lt;code&gt;Git&lt;/code&gt;, &lt;code&gt;wget&lt;/code&gt;, &lt;code&gt;curl&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NVM&lt;/code&gt; and multiple versions of &lt;code&gt;Node.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Package managers like &lt;code&gt;PNPM&lt;/code&gt;, &lt;code&gt;YARN&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Programming languages like &lt;code&gt;Python&lt;/code&gt;, &lt;code&gt;Ruby&lt;/code&gt;, &lt;code&gt;Go&lt;/code&gt;, &lt;code&gt;Java&lt;/code&gt;, &lt;code&gt;Rust&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Databases like &lt;code&gt;PostgreSQL&lt;/code&gt; and extensions&lt;/li&gt;
&lt;li&gt;Customised shells (e.g., &lt;code&gt;Zsh&lt;/code&gt; with themes from &lt;a href="https://github.com/ohmyzsh/ohmyzsh/wiki/Themes" rel="noopener noreferrer"&gt;Oh My Zsh&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Other customised software or settings&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Pre-installed VS Code extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specific extensions and versions (including pre-releases)&lt;/li&gt;
&lt;li&gt;Self-made extensions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Pre-configured VS Code settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tab size: 2&lt;/li&gt;
&lt;li&gt;Auto-insert spaces on hitting &lt;code&gt;Tab&lt;/code&gt;: true&lt;/li&gt;
&lt;li&gt;Font size and family&lt;/li&gt;
&lt;li&gt;And more...&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Automatic workflow scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatically run &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;npm start&lt;/code&gt; or other scripts as needed&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Develop remotely without a PC!&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;&lt;p&gt;And more...&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Steps to Use Dev Containers
&lt;/h2&gt;

&lt;p&gt;I have several guides on setting up and using Dev Containers, each brief and easy to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dev Containers - Why You Need Them&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-1-quick-start-basic-setup-and-usage-51l6"&gt;Part 1: Quick Start - Basic Setup and Usage&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-2-image-features-workspace-environment-variables-375o"&gt;Part 2: Image, Features, Workspace, Environment Variables&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-3-full-stack-dev-docker-compose-database-5f5o"&gt;Part 3: Full Stack Dev - Docker Compose &amp;amp; Database&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-4-remote-dev-develop-on-a-remote-docker-host-440l"&gt;Part 4: Remote Dev - Develop on a Remote Docker Host&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/graezykev/dev-containers-part-5-multiple-projects-shared-container-configuration-2hoi"&gt;Part 5: Multiple Projects &amp;amp; Shared Container Configuration&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devcontainer</category>
    </item>
    <item>
      <title>A Guide to Understanding the Nuances of Git Merge and Rebase</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Tue, 14 May 2024 09:24:38 +0000</pubDate>
      <link>https://dev.to/graezykev/a-guide-to-understanding-the-nuances-of-git-merge-and-rebase-k2e</link>
      <guid>https://dev.to/graezykev/a-guide-to-understanding-the-nuances-of-git-merge-and-rebase-k2e</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I've seen many developers &lt;strong&gt;avoid using rebase&lt;/strong&gt; due to its complexity. I've also noticed that some teams even forbid rebase in their projects altogether.&lt;/p&gt;

&lt;p&gt;In this guide, we will walk through the basics of managing code changes in Git using both merge and rebase strategies. This tutorial aims to clarify these often misunderstood commands with a hands-on demonstration.&lt;/p&gt;

&lt;p&gt;I may be too verbose in walking you through these steps one by one, but I suggest you start from the Recap section, or jump straight to the Key Takeaways section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Scenario Setup

&lt;ul&gt;
&lt;li&gt;Initialise a Git Repository and Create a Starting File&lt;/li&gt;
&lt;li&gt;Make Initial Commits A and B&lt;/li&gt;
&lt;li&gt;Bseline Commit Graph&lt;/li&gt;
&lt;li&gt;Create a Feature Branch&lt;/li&gt;
&lt;li&gt;Commit C&lt;/li&gt;
&lt;li&gt;Commit D (Developing on Feature Branch)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Initial Git Commit Graph&lt;/li&gt;

&lt;li&gt;Decision Time: Merging vs. Rebasing&lt;/li&gt;

&lt;li&gt;

Option 1: Merge feature into main

&lt;ul&gt;
&lt;li&gt;Merge&lt;/li&gt;
&lt;li&gt;Merge Conflict&lt;/li&gt;
&lt;li&gt;Graph After Merge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Option 2: Rebase feature onto main

&lt;ul&gt;
&lt;li&gt;Rebase&lt;/li&gt;
&lt;li&gt;Rebase Conflict&lt;/li&gt;
&lt;li&gt;Graph After Rebase&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Recap

&lt;ul&gt;
&lt;li&gt;Commit History&lt;/li&gt;
&lt;li&gt;Commit Graph of Taking Each Option&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Key Takeaways: Merge or Rebase?&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Scenario Setup
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Initialise a Git Repository and Create a Starting File
&lt;/h3&gt;

&lt;p&gt;First, create a new directory and initialise a Git repository:&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;mkdir &lt;/span&gt;git-nuance-demo-merge &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;git-nuance-demo-merge &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git branch &lt;span class="nt"&gt;-M&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a text file named &lt;code&gt;example.txt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;example.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Make Initial Commits &lt;code&gt;A&lt;/code&gt; and &lt;code&gt;B&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Now, add a first line to &lt;code&gt;example.txt&lt;/code&gt; and commit it as Commit &lt;code&gt;A&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+Initial content
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Proceed by adding a second line and commit it as Commit &lt;code&gt;B&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;Initial content
&lt;/span&gt;&lt;span class="gi"&gt;+Added by leader
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bseline Commit Graph
&lt;/h3&gt;

&lt;p&gt;Now we can visualize the project's &lt;strong&gt;baseline&lt;/strong&gt; with this commit graph:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;A---B&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a Feature Branch
&lt;/h3&gt;

&lt;p&gt;Assuming developer Kev needs to add a new feature, he branches off from &lt;code&gt;main&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;git checkout main &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Commit &lt;code&gt;C&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While Kev works on the feature branch, another developer, Dash, updates the &lt;code&gt;main&lt;/code&gt; branch. Dash modifies the &lt;strong&gt;second&lt;/strong&gt; line in Commit &lt;code&gt;C&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;git checkout main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;Initial content
&lt;/span&gt;&lt;span class="gd"&gt;-Added by leader
&lt;/span&gt;&lt;span class="gi"&gt;+Modified by Dash
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Commit &lt;code&gt;D&lt;/code&gt; (Developing on Feature Branch)
&lt;/h3&gt;

&lt;p&gt;Simultaneously, Kev modifies the &lt;strong&gt;same&lt;/strong&gt; line on his branch in Commit &lt;code&gt;D&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;git checkout feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;Initial content
&lt;/span&gt;&lt;span class="gd"&gt;-Added by leader
&lt;/span&gt;&lt;span class="gi"&gt;+Modified by Kev
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Initial Git Commit Graph
&lt;/h2&gt;

&lt;p&gt;The commit graph is now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;A---B---C&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
     &lt;span class="err"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;D&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And check their Git history details by running &lt;code&gt;git log main&lt;/code&gt; and &lt;code&gt;git log feature&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The commit IDs (hashes) will be different if you try following my steps from the beginning on your device.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;main&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;/workspaces/git-nuance-demo-merge (main) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log main
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;commit ac466bea2d13762608660e27798ba08b840529db (HEAD -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;Author: Dash &amp;lt;dash@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    C

commit fd6ecc11625c41c06e150015254b16a620c1cd44
&lt;/span&gt;&lt;span class="gp"&gt;Author: Leader &amp;lt;leader@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    B

commit 9d00689a138f8d1fd6b7d370bae29bcfb27fec19
&lt;/span&gt;&lt;span class="gp"&gt;Author: Leader &amp;lt;leader@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    A

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feature&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;/workspaces/git-nuance-demo-merge (main) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log feature
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;commit 58beb1e4f1d47f9bf1364be906a2e22b5280be1d (HEAD -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;feature&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;Author: Kev &amp;lt;kev@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    D

commit fd6ecc11625c41c06e150015254b16a620c1cd44
&lt;/span&gt;&lt;span class="gp"&gt;Author: Leader &amp;lt;leader@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    B

commit 9d00689a138f8d1fd6b7d370bae29bcfb27fec19
&lt;/span&gt;&lt;span class="gp"&gt;Author: Leader &amp;lt;leader@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    A

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Decision Time: Merging vs. Rebasing
&lt;/h2&gt;

&lt;p&gt;Imagine Kev needs to release the features in branch &lt;code&gt;feature&lt;/code&gt;, he faces a decision: to merge or rebase his feature branch onto the updated main branch.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Merge&lt;/code&gt;: merge &lt;code&gt;feature&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt; and release &lt;code&gt;main&lt;/code&gt; to production environment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Rebase&lt;/code&gt;: Rebase &lt;code&gt;feature&lt;/code&gt; onto &lt;code&gt;main&lt;/code&gt;, and release &lt;code&gt;feature&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We all know that Kev and Dash both edited the &lt;strong&gt;second&lt;/strong&gt; line, meaning a &lt;strong&gt;conflict&lt;/strong&gt; is definitely going to happen in both options.&lt;/p&gt;

&lt;p&gt;Next, we're going to apply both options and examine the different consequences of each.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Before continuing, copy the initial status into two separate folders so that we can apply each option in its own folder:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; .. &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; git-nuance-demo-merge git-nuance-demo-rebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Option 1: Merge &lt;code&gt;feature&lt;/code&gt; into &lt;code&gt;main&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Merge
&lt;/h3&gt;

&lt;p&gt;Merging results in a new commit on &lt;code&gt;main&lt;/code&gt; that combines the changes from both 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="nb"&gt;cd &lt;/span&gt;git-nuance-demo-merge
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git merge feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JvZL3kET--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/merge-c.gif" class="article-body-image-wrapper"&gt;&lt;img alt="merge feature into main" src="https://res.cloudinary.com/practicaldev/image/fetch/s--JvZL3kET--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/merge-c.gif" width="643" height="513"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Merge Conflict
&lt;/h3&gt;

&lt;p&gt;This approach creates a merge conflict that Kev resolves by favouring his changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Initial content
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Modified by Dash
=======
Modified by Kev
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pay attention to the terminal console:&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="gp"&gt;/workspaces/git-nuance-demo-merge (main) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git merge feature
&lt;span class="go"&gt;Auto-merging example.txt
CONFLICT (content): Merge conflict in example.txt
&lt;/span&gt;&lt;span class="gp"&gt;Automatic merge failed;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fix conflicts and &lt;span class="k"&gt;then &lt;/span&gt;commit the result.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check this out: "&lt;strong&gt;fix conflicts and then commit the result&lt;/strong&gt;"&lt;/p&gt;

&lt;p&gt;It instructs us to both &lt;strong&gt;fix&lt;/strong&gt; and &lt;strong&gt;commit&lt;/strong&gt; so that the conflicts can be resolved. This means a &lt;strong&gt;new commit&lt;/strong&gt; is needed when resolving merge conflicts.&lt;/p&gt;

&lt;p&gt;Resolve it by selecting "&lt;strong&gt;Accept Incoming Change&lt;/strong&gt;", which in this case means accepting the changes from the &lt;code&gt;feature&lt;/code&gt; branch.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1q26Znmo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/merge-c-2.gif" class="article-body-image-wrapper"&gt;&lt;img alt="fix git merge conflict and commit" src="https://res.cloudinary.com/practicaldev/image/fetch/s--1q26Znmo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/merge-c-2.gif" width="643" height="836"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you check the log by running &lt;code&gt;git log main&lt;/code&gt;, you'll notice the new merge commit in the logs saying "&lt;strong&gt;Merge branch..&lt;/strong&gt;.":&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;/workspaces/git-nuance-demo-merge (main) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log main
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;commit 4d6ac5eb811be1a6130c6bbcde215946c618dd95 (HEAD -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;main&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;Merge: ac466be 58beb1e
&lt;/span&gt;&lt;span class="gp"&gt;Author: Kev &amp;lt;kev@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    Merge branch 'feature'

commit 58beb1e4f1d47f9bf1364be906a2e22b5280be1d (feature)
&lt;/span&gt;&lt;span class="gp"&gt;Author: Kev &amp;lt;kev@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    D

commit ac466bea2d13762608660e27798ba08b840529db
&lt;/span&gt;&lt;span class="gp"&gt;Author: Dash &amp;lt;dash@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    C

commit fd6ecc11625c41c06e150015254b16a620c1cd44
&lt;/span&gt;&lt;span class="gp"&gt;Author: Leader &amp;lt;leader@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    B

&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wondering what the change is in this new commit?&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;git diff 4d6ac5e~ 4d6ac5e&lt;/code&gt; (4d6ac5e is the commit ID (hash))&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;/workspaces/git-nuance-demo-merge (main) $ git diff 4d6ac5e~ 4d6ac5e
&lt;span class="gh"&gt;diff --git a/example.txt b/example.txt
index 22ad65d..d49a266 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/example.txt
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/example.txt
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,2 +1,2 @@&lt;/span&gt;
 Initial content
&lt;span class="gd"&gt;-Modified by Dash
&lt;/span&gt;&lt;span class="gi"&gt;+Modified by Kev
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you take a deeper look at the details of this new commit, pay special attention to the line:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Merge: &lt;code&gt;ac466be&lt;/code&gt; &lt;code&gt;58beb1e&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;What does it mean?&lt;/p&gt;

&lt;p&gt;This &lt;strong&gt;merge&lt;/strong&gt; commit has two &lt;strong&gt;parents&lt;/strong&gt;, pointing to commit &lt;code&gt;C&lt;/code&gt; and commit &lt;code&gt;D&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SPeaVl7S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/image-10.png" class="article-body-image-wrapper"&gt;&lt;img alt="merge commit parents" src="https://res.cloudinary.com/practicaldev/image/fetch/s--SPeaVl7S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/image-10.png" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Graph After Merge
&lt;/h3&gt;

&lt;p&gt;After resolving the conflict, the commit graph looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;A---B---C---M&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
     &lt;span class="err"&gt;\&lt;/span&gt;       &lt;span class="o"&gt;/&lt;/span&gt;
      &lt;span class="nt"&gt;D&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Option 2: Rebase &lt;code&gt;feature&lt;/code&gt; onto &lt;code&gt;main&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Before continuing with the rebase process, take a look at the &lt;code&gt;feature&lt;/code&gt; branch's logs at this point:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;/workspaces/git-nuance-demo-merge (main) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log feature
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;commit 58beb1e4f1d47f9bf1364be906a2e22b5280be1d (Head -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;feature&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;Author: Kev &amp;lt;kev@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    D
&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commit &lt;code&gt;D&lt;/code&gt;'s ID (hash) is &lt;code&gt;58beb1e....&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The change in this commit is:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;/workspaces/git-nuance-demo-merge (main) $ git diff 58beb1e~ 58beb1e
&lt;span class="gh"&gt;diff --git a/example.txt b/example.txt
index a53a0e2..d49a266 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/example.txt
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/example.txt
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,2 +1,2 @@&lt;/span&gt;
 Initial content
&lt;span class="gd"&gt;-Added by leader
&lt;/span&gt;&lt;span class="gi"&gt;+Modified by Kev
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The commit ID and changes will be altered after the rebase; we'll see why.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase
&lt;/h3&gt;

&lt;p&gt;Let's now start the &lt;strong&gt;rebase&lt;/strong&gt; process.&lt;/p&gt;

&lt;p&gt;Alternatively to &lt;strong&gt;option 1&lt;/strong&gt;, rebasing involves integrating the changes from &lt;code&gt;main&lt;/code&gt; directly into the &lt;code&gt;feature&lt;/code&gt; branch history.&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;cd &lt;/span&gt;git-nuance-demo-rebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout feature
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W922qRZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/rebase-c.gif" class="article-body-image-wrapper"&gt;&lt;img alt="rebase feature onto main" src="https://res.cloudinary.com/practicaldev/image/fetch/s--W922qRZq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/rebase-c.gif" width="643" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rebase Conflict
&lt;/h3&gt;

&lt;p&gt;It creates a merge conflict that is almost identical to the one in &lt;strong&gt;option 1&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;Initial content
&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD
Modified by Dash
=======
Modified by Kev
&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt;&amp;gt; 58beb1e (D)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what's in the terminal console is different:&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="gp"&gt;/workspaces/git-nuance-demo-rebase (feature) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git rebase main
&lt;span class="go"&gt;Auto-merging example.txt
CONFLICT (content): Merge conflict in example.txt
error: could not apply 58beb1e... D
hint: Resolve all conflicts manually, mark them as resolved with
&lt;/span&gt;&lt;span class="gp"&gt;hint: "git add/rm &amp;lt;conflicted_files&amp;gt;&lt;/span&gt;&lt;span class="s2"&gt;", then run "&lt;/span&gt;git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;&lt;span class="s2"&gt;".
&lt;/span&gt;&lt;span class="go"&gt;hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply 58beb1e... D
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It instructs us to "&lt;strong&gt;Resolve all conflicts manually&lt;/strong&gt;", and continue with &lt;code&gt;git rebase --continue&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Alright, let's follow the hint to resolve the conflict (also by selecting "Accept Incoming Change").&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add example.txt &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ro-y6SgC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/rebase-c-2.gif" class="article-body-image-wrapper"&gt;&lt;img alt="fix git rebase conflict and commit" src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ro-y6SgC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/rebase-c-2.gif" width="643" height="855"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Take a look at the content after running &lt;code&gt;git rebase --continue&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;D

# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# interactive rebase in progress; onto 05cfdd2
# Last command done (1 command done):
#    pick 58beb1e D
# No commands remaining.
# You are currently rebasing branch 'feature' on '05cfdd2'.
#
# Changes to be committed:
# modified:   example.txt
#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git offers us an editor to edit the commit message of &lt;code&gt;D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can rewrite commit &lt;code&gt;D&lt;/code&gt; by editing the message (lines starting with "#" will be ignored).&lt;/p&gt;

&lt;p&gt;I made no changes and just closed the commit editor, so the rebase process proceeded with the &lt;strong&gt;rewriting&lt;/strong&gt; of commit &lt;code&gt;D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that the rebase process has finished, take a look at the commit history of the &lt;code&gt;feature&lt;/code&gt; branch:&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="gp"&gt;/workspaces/git-nuance-demo-rebase (feature) $&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git log feature
&lt;span class="go"&gt;
&lt;/span&gt;&lt;span class="gp"&gt;commit 047c3480da20d9dd6c530d3698b4e047f1f1d9d9 (HEAD -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;feature&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;Author: Kev &amp;lt;kev@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    D

commit ac466bea2d13762608660e27798ba08b840529db (main)
&lt;/span&gt;&lt;span class="gp"&gt;Author: Dash &amp;lt;dash@test.com&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="go"&gt;
    C

&lt;/span&gt;&lt;span class="c"&gt;...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I2zm9D5d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/image-11.png" class="article-body-image-wrapper"&gt;&lt;img alt="Git Logs" src="https://res.cloudinary.com/practicaldev/image/fetch/s--I2zm9D5d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/graezykev/git-nuance-merge-rebase/main/image-11.png" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pay special attention to the commit ID of the &lt;strong&gt;new&lt;/strong&gt; &lt;code&gt;D&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It's now &lt;code&gt;047c348...&lt;/code&gt;. Remember what it was before the rebase? It was &lt;code&gt;58beb1e...&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And the change in this commit is:&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;/workspaces/git-nuance-demo-rebase (feature) $ git diff 047c348~ 047c348
&lt;span class="gh"&gt;diff --git a/example.txt b/example.txt
index 22ad65d..d49a266 100644
&lt;/span&gt;&lt;span class="gd"&gt;--- a/example.txt
&lt;/span&gt;&lt;span class="gi"&gt;+++ b/example.txt
&lt;/span&gt;&lt;span class="p"&gt;@@ -1,2 +1,2 @@&lt;/span&gt;
 Initial content
&lt;span class="gd"&gt;-Modified by Dash
&lt;/span&gt;&lt;span class="gi"&gt;+Modified by Kev
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See? The change is also different from the previous commit &lt;code&gt;D&lt;/code&gt; because the commit &lt;code&gt;D&lt;/code&gt; was &lt;strong&gt;rewritten&lt;/strong&gt; in the rebase process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Graph After Rebase
&lt;/h3&gt;

&lt;p&gt;Rebase modifies the original commit &lt;code&gt;D&lt;/code&gt; on the feature branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;A---B---C&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
         &lt;span class="err"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;D&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we performed a &lt;strong&gt;history rewrite&lt;/strong&gt;, causing the original commit &lt;code&gt;D&lt;/code&gt; to disappear and the new commit &lt;code&gt;D'&lt;/code&gt; to emerge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Commit History
&lt;/h3&gt;

&lt;p&gt;Now let's recap the primary steps and Git histories in both options we covered.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;td&gt;Option 1: Merge&lt;/td&gt;
      &lt;td&gt;Option 2: Rebase&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;td&gt;Commit&lt;/td&gt;
      &lt;td&gt;Diff&lt;/td&gt;
      &lt;td&gt;Author&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;
Merge
&lt;/td&gt;

&lt;td&gt;



```diff

-Modified by Dash
+Modified by Kev

```



&lt;/td&gt;

&lt;td&gt;
Kev
&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;
D
&lt;/td&gt;

&lt;td&gt;



```diff

-Add by leader
+Modified by Kev

```



&lt;/td&gt;

&lt;td&gt;
Kev
&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;
C
&lt;/td&gt;

&lt;td&gt;



```diff

-Add by leader
+Modified by Dash

```



&lt;/td&gt;

&lt;td&gt;
Dash
&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;



```diff

+Add by leader

```



&lt;/td&gt;
&lt;td&gt;Leader&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
  &lt;td&gt;A&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;Leader&lt;/td&gt;
&lt;/tr&gt;

  &lt;/tbody&gt;
&lt;/table&gt;




&lt;/td&gt;




&lt;td&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;td&gt;Commit&lt;/td&gt;
      &lt;td&gt;Diff&lt;/td&gt;
      &lt;td&gt;Author&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;

&lt;tr&gt;

&lt;td&gt;
D'
&lt;/td&gt;

&lt;td&gt;



```diff

-Modified by Dash
+Modified by Kev

```



&lt;/td&gt;

&lt;td&gt;
Kev
&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;
D (actually disappeared)
&lt;/td&gt;

&lt;td&gt;



```diff

-Add by leader
+Modified by Kev

```



&lt;/td&gt;

&lt;td&gt;
Kev
&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;

&lt;td&gt;
C
&lt;/td&gt;

&lt;td&gt;



```diff

-Add by leader
+Modified by Dash

```



&lt;/td&gt;

&lt;td&gt;
Dash
&lt;/td&gt;

&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;td&gt;



```diff

+Add by leader

```



&lt;/td&gt;
&lt;td&gt;Leader&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
  &lt;td&gt;A&lt;/td&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;Leader&lt;/td&gt;
&lt;/tr&gt;

  &lt;/tbody&gt;
&lt;/table&gt;




&lt;/td&gt;




&lt;/tr&gt;




&lt;/tbody&gt;
&lt;br&gt;
&lt;br&gt;&lt;br&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;You can also view the histories in my demo repositories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Option 1: &lt;a href="https://github.com/graezykev/git-nuance-demo-merge/commits/main/" rel="noopener noreferrer"&gt;Merge&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Option 2: &lt;a href="https://github.com/graezykev/git-nuance-demo-rebase/commits/feature" rel="noopener noreferrer"&gt;Rebase&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Commit Graph of Taking Each Option
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Merge&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;A---B---C---M&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
     &lt;span class="err"&gt;\&lt;/span&gt;       &lt;span class="o"&gt;/&lt;/span&gt;
      &lt;span class="nt"&gt;D&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rebase&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;A---B---C&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;main&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
         &lt;span class="err"&gt;\&lt;/span&gt;
          &lt;span class="nt"&gt;D&lt;/span&gt;&lt;span class="err"&gt;'&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The obvious difference is that there are &lt;strong&gt;five&lt;/strong&gt; commits (&lt;code&gt;A-B-C-D-M&lt;/code&gt;) after a &lt;strong&gt;merge&lt;/strong&gt;, and &lt;strong&gt;four&lt;/strong&gt; (&lt;code&gt;A-B-C-D'&lt;/code&gt;) after a &lt;strong&gt;rebase&lt;/strong&gt; (D disappears because it is rewritten).&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Rebase can be Dangerous
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Merge&lt;/strong&gt; preserves the exact historical sequence of changes and is generally easier for beginners to understand and handle, especially in a collaborative environment.&lt;/p&gt;

&lt;p&gt;In contrast, &lt;strong&gt;Rebase&lt;/strong&gt; rewrites the commit history to make it appear as if your changes were created on top of the latest remote commits.&lt;/p&gt;

&lt;p&gt;This can result in a cleaner, more linear commit history but can be confusing because it alters the commit history. This might be trickier in a collaborative project unless all contributors are comfortable with Git.&lt;/p&gt;

&lt;p&gt;This is typical evidence of why &lt;strong&gt;Git rebase can be dangerous&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;In more complex, real-world scenarios, there is a risk that you could rewrite, overwrite, or delete another developer's commit from the history, preventing your teammates from finding their previously made commits.&lt;/p&gt;

&lt;p&gt;There are some basic principles in using rebase like "&lt;strong&gt;Don’t rebase a branch that’s been published remotely&lt;/strong&gt;" and "&lt;strong&gt;Create a backup branch from the tip of the branch you’re about to rebase&lt;/strong&gt;" etc.&lt;/p&gt;

&lt;p&gt;There are some basic principles for using rebase, such as "&lt;strong&gt;Don’t rebase a branch that’s been published remotely&lt;/strong&gt;" and "&lt;strong&gt;Create a backup branch from the tip of the branch you’re about to rebase&lt;/strong&gt;".&lt;/p&gt;

&lt;p&gt;Choosing between merge and rebase based on your project's needs is a broad topic that is not comprehensively covered within the scope of this post. For more detailed information, I recommend reading "&lt;a href="https://blog.git-init.com/differences-between-git-merge-and-rebase-and-why-you-should-care/#conclusion" rel="noopener noreferrer"&gt;Differences Between Git Merge and Rebase — and Why You Should Care&lt;/a&gt;".&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways: Merge or Rebase?
&lt;/h2&gt;

&lt;p&gt;Both strategies have their merits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Merge&lt;/code&gt;: You create a &lt;strong&gt;new commit&lt;/strong&gt; that shows how conflicts were resolved, allowing your teammates to clearly see the resolution process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;Rebase&lt;/code&gt;: You &lt;strong&gt;modify existing commits&lt;/strong&gt;. When resolving conflicts during a rebase, you may alter code or commit messages that others have previously made. This can disrupt collaborative work if not used carefully.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>merge</category>
      <category>rebase</category>
    </item>
    <item>
      <title>A doggerel rhyme to ensure you won't get confused by HTTP 301 and 302</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Fri, 10 May 2024 10:30:55 +0000</pubDate>
      <link>https://dev.to/graezykev/a-doggerel-rhyme-to-ensure-you-wont-get-confused-by-http-301-and-302-32l4</link>
      <guid>https://dev.to/graezykev/a-doggerel-rhyme-to-ensure-you-wont-get-confused-by-http-301-and-302-32l4</guid>
      <description>&lt;p&gt;&lt;strong&gt;Three-O-One, the permanent run;&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Three-O-Two, just passing through.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;301 Moved Permanently&lt;/strong&gt;: Imagine your favourite restaurant has moved to a new location. You know you won't find it at the old address anymore, so you update your address book and GPS to go directly to the new spot. It's like the restaurant has permanently changed its address.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;302 Found (Moved Temporarily)&lt;/strong&gt;: picture a food truck that occasionally changes its parking spot. You might find it at one location today and another tomorrow. You keep checking the usual spot because you know it's just a temporary move.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/301" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqry8w8q95293vbsydsd9.png" alt="HTTP 301" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhhgzeqdwa8ugw6vizjmi.png" alt="HTTP 302" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>301</category>
      <category>302</category>
      <category>http</category>
    </item>
    <item>
      <title>Streamline Your Workflow: A Guide to Normalising Git Commit and Push Processes</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Mon, 06 May 2024 03:07:45 +0000</pubDate>
      <link>https://dev.to/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d</link>
      <guid>https://dev.to/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d</guid>
      <description>&lt;p&gt;Discover how to streamline your Git commit and push processes using a robust toolkit comprising &lt;code&gt;Husky&lt;/code&gt;, &lt;code&gt;ESLint&lt;/code&gt;, &lt;code&gt;lint-staged&lt;/code&gt;, &lt;code&gt;Commitlint&lt;/code&gt;, and simple &lt;code&gt;Shell&lt;/code&gt; scripts. This guide will help you automate and standardise your version control workflow, ensuring consistency and efficiency in your development projects. Dive into practical setups and tips to make your Git operations seamless and error-free.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Today's topic revolves around leveraging Git Hooks to streamline Git workflows, focusing on code linting before commits and pushes, and standardizing the format of commit messages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Git Hooks&lt;/strong&gt; are essentially &lt;strong&gt;custom scripts&lt;/strong&gt; that trigger during key Git actions.&lt;/p&gt;

&lt;p&gt;There are two categories of Git Hooks: &lt;strong&gt;client-side&lt;/strong&gt; and &lt;strong&gt;server-side&lt;/strong&gt;. Client-side hooks are activated by operations such as committing, merging, and pushing, whereas server-side hooks are triggered by network operations, like receiving pushed commits.&lt;/p&gt;

&lt;p&gt;In this post, I will primarily discuss client-side hooks. I'll delve into three specific hooks: &lt;strong&gt;pre-commit&lt;/strong&gt;, &lt;strong&gt;commit-msg&lt;/strong&gt;, and &lt;strong&gt;pre-push&lt;/strong&gt;. The central tool discussed will be &lt;a href="https://typicode.github.io/husky/" rel="noopener noreferrer"&gt;Husky&lt;/a&gt;, which simplifies the configuration of Git Hooks, making it more straightforward.&lt;/p&gt;

&lt;p&gt;Here’s a preview of what I will cover in this post:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Dk0AIR-d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.mermaidchart.com/raw/f4d8ba46-56ff-42a7-8637-73f29036db28%3Ftheme%3Dlight%26version%3Dv0.1%26format%3Dsvg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Dk0AIR-d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.mermaidchart.com/raw/f4d8ba46-56ff-42a7-8637-73f29036db28%3Ftheme%3Dlight%26version%3Dv0.1%26format%3Dsvg" alt="svg" width="607" height="1340"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  GitHub Repo
&lt;/h3&gt;

&lt;p&gt;If you'd prefer to run the demo I've created instead of following the steps individually, check out this &lt;a href="https://github.com/graezykev/normalise-your-git-commit-and-push" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for a quick overview and hands-on experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;p&gt;I would like to outline all the steps I'm gonna elaborate on in this post.&lt;/p&gt;

&lt;p&gt;Tedious? Don't worry, each step is clear and straightforward. &lt;strong&gt;Most of the time, you can copy and paste my commands and codes to follow the guide&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;1 - Init your Project&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;2 - Git Pre-Commit Hook&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2.1 - Create Pre-Commit Hook
&lt;/li&gt;
&lt;li&gt;2.2 - Code Linting
&lt;/li&gt;
&lt;li&gt;2.3 - Integrate Linting into the Pre-Commit Hook
&lt;/li&gt;
&lt;li&gt;2.4 - Brief Sum-Up
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;3 - lint-staged&lt;/p&gt;&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;4 - Commit Message Hook&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;4.1 - Commit Message Format Linting
&lt;/li&gt;
&lt;li&gt;4.2 - Add Message Format Linting to Commit Message Hook
&lt;/li&gt;
&lt;li&gt;4.3 - Tailor your Commit Message Format
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;5 - Git Push Hook&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;5.1 - Incremental Code Linting
&lt;/li&gt;
&lt;li&gt;5.2 - Force test Before Push
&lt;/li&gt;
&lt;li&gt;5.3 - Force Push
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Init your Project (if you haven't)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Init NPM
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;normalise-your-git-commit-and-push &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;normalise-your-git-commit-and-push &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
npm init &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
npm pkg &lt;span class="nb"&gt;set type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Init Git Repo
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'node_modules'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  2. Git Pre-Commit Hook
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Git Pre-Commit Hook&lt;/strong&gt; is triggered just after you execute the &lt;code&gt;git commit&lt;/code&gt; command, but before the commit message editor is opened (if you are not using the &lt;code&gt;-m&lt;/code&gt; option), or before the commit is finalized (if you are using the &lt;code&gt;-m&lt;/code&gt; option).&lt;/p&gt;

&lt;p&gt;What is commit message editor?&lt;/p&gt;

&lt;p&gt;The most simple way of specifying our commit message is to execute &lt;code&gt;git commit -m 'my commit message'&lt;/code&gt;. However, there's a more interactive way to commit using &lt;code&gt;git commit&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xbknko07bz8lyzp25hs.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0xbknko07bz8lyzp25hs.gif" alt="commit message editor" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After you run &lt;code&gt;git commit&lt;/code&gt; without the &lt;code&gt;-m&lt;/code&gt; option and commit message, Git typically opens an editor for you to enter a commit message (This editor could be Vim, Emacs, Nano, or whatever your default command-line text editor is set to).&lt;/p&gt;

&lt;p&gt;Returning to the &lt;strong&gt;Git Pre-Commit Hook&lt;/strong&gt;, it is triggered before the editor is opened, or before Git receives you commit message if you're using &lt;code&gt;-m&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can use it to perform checks (such as running the ESLint command), and if these checks fail, the commit action is blocked.&lt;/p&gt;

&lt;p&gt;By sharing this &lt;strong&gt;hook script&lt;/strong&gt;, you can ensure that every teammate commits only code that has been checked.&lt;/p&gt;

&lt;p&gt;Let's explore how we can accomplish this.&lt;/p&gt;
&lt;h3&gt;
  
  
  2.1. Create Pre-Commit Hook
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Install Husky
&lt;/h4&gt;

&lt;p&gt;The first important step is to install Husky.&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;-D&lt;/span&gt; husky@9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Init Husky
&lt;/h4&gt;


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

&lt;/div&gt;


&lt;p&gt;It's necessary to break down what primarily happens after the execution of &lt;code&gt;husky init&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the directory &lt;code&gt;.husky/_&lt;/code&gt; and add the configuration of &lt;code&gt;hooksPath = .husky/_&lt;/code&gt; to &lt;code&gt;.git/config&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;.husky/_&lt;/code&gt; contains the actual hook scripts that Husky will execute in response to specific Git events.&lt;/p&gt;

&lt;p&gt;By setting &lt;code&gt;hooksPath = .husky/_&lt;/code&gt;, it tells Git to look for hooks in the &lt;code&gt;.husky/_&lt;/code&gt; directory instead of the default &lt;code&gt;.git/hooks&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create executable scripts such as &lt;code&gt;.husky/_/husky.sh&lt;/code&gt; and &lt;code&gt;.husky/_/h&lt;/code&gt; etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;.husky/_/husky.sh&lt;/code&gt; is the primary script that Husky injects into the hooks it manages, and &lt;code&gt;.husky/_/h&lt;/code&gt; is its Shortcut.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a script &lt;code&gt;.husky/pre-commit&lt;/code&gt; with the command &lt;code&gt;npm test&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This executable file is the actual &lt;strong&gt;Git Pre-Commit Hook&lt;/strong&gt; we mentioned before, whatever &lt;strong&gt;user-defined&lt;/strong&gt; behaviours you want to add should be placed here.&lt;/p&gt;

&lt;p&gt;It's invoked by &lt;code&gt;.husky/_/husky.sh&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;npm test&lt;/code&gt; in it means: You want to commit the code? Oh, you have to pass the test first.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the &lt;code&gt;prepare&lt;/code&gt; command in &lt;code&gt;package.json&lt;/code&gt; with the execution of &lt;code&gt;husky&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Anyone who clones your Git repo and runs &lt;code&gt;npm install&lt;/code&gt; will automatically invoke the &lt;code&gt;prepare&lt;/code&gt; script. Then, &lt;code&gt;husky&lt;/code&gt; will initiate &lt;code&gt;.husky/_&lt;/code&gt; and configure &lt;code&gt;.git/hooks&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a &lt;code&gt;.gitignore&lt;/code&gt; in &lt;code&gt;.husky/_&lt;/code&gt; with a single line &lt;code&gt;*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents everything inside &lt;code&gt;.husky/_&lt;/code&gt; from being pushed to the remote repository.&lt;/p&gt;

&lt;p&gt;Why should &lt;code&gt;.husky/_&lt;/code&gt; be excluded from Git version control?&lt;/p&gt;

&lt;p&gt;The scripts under &lt;code&gt;.husky/_&lt;/code&gt; should be generated dynamically according to different environments' configurations and behaviors (machines, operating systems, CI environments, etc.).&lt;/p&gt;

&lt;p&gt;For example, the Windows system uses &lt;code&gt;\&lt;/code&gt; as a path separator, while Linux uses &lt;code&gt;/&lt;/code&gt;. Each system may have &lt;code&gt;Node.js&lt;/code&gt; installed in a specific path. Each system may have its own &lt;code&gt;Node.js&lt;/code&gt; version.&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;You cannot create scripts that can be universally applied unless you're working inside the specific environment.&lt;/p&gt;
&lt;h4&gt;
  
  
  Try A Commit
&lt;/h4&gt;

&lt;p&gt;Since our &lt;code&gt;pre-commit&lt;/code&gt; hook has been initiated, let's try it.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'first commit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here is what you'll see from the terminal console:&lt;/p&gt;

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

&lt;p&gt;It fails because the pre-commit hook runs the &lt;code&gt;test&lt;/code&gt; job, which contains an &lt;code&gt;exit 1&lt;/code&gt;, in the &lt;code&gt;test&lt;/code&gt; script of &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="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;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;Error: no test specified&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Changing it to &lt;code&gt;exit 0&lt;/code&gt; will make the commit work.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;"scripts": {
&lt;span class="gd"&gt;-  "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1",
&lt;/span&gt;&lt;span class="gi"&gt;+  "test": "exit 0",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Commit again:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'first commit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;In a real production project, you should specify your actual &lt;code&gt;test&lt;/code&gt; command, such as Jest, Playwright, etc.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  2.2. Code Linting
&lt;/h3&gt;

&lt;p&gt;For now, our &lt;strong&gt;pre-commit&lt;/strong&gt; hook only includes a &lt;code&gt;test&lt;/code&gt; command. Next, we'll add a Lint command to check the &lt;strong&gt;Code Style&lt;/strong&gt; before you commit JavaScript code.&lt;/p&gt;
&lt;h4&gt;
  
  
  Install &amp;amp; Configure Linting Tools
&lt;/h4&gt;

&lt;p&gt;Before Linting code in pre-commit hook, let's install and configure ESLint.&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;-D&lt;/span&gt; eslint@9 @eslint/js@9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Create an &lt;code&gt;eslint.config.js&lt;/code&gt; file with the code below:&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;// eslint.config.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pluginJs&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;@eslint/js&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="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;pluginJs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recommended&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: I use &lt;code&gt;export default xxx&lt;/code&gt; here because my &lt;code&gt;package.json&lt;/code&gt; includes the configuration &lt;code&gt;"type": "module"&lt;/code&gt;. If you don't have this configuration, use &lt;code&gt;module.exports = xxx&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  Add Linting Script to &lt;code&gt;package.json&lt;/code&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;"scripts": {
&lt;span class="gi"&gt;+  "lint": "eslint .",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Create a Demo &lt;code&gt;index.js&lt;/code&gt;
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;field&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;evn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bit&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;h4&gt;
  
  
  Lint the Demo
&lt;/h4&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This will produce some errors because we haven't defined the variable &lt;code&gt;process&lt;/code&gt; in the demo code, which is not allowed according to the ESLint rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnxpj9uf83i3p0kcbufh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffnxpj9uf83i3p0kcbufh.png" alt="Git Hook" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2.3. Integrate Linting into the Pre-Commit Hook
&lt;/h3&gt;

&lt;p&gt;In the last step, we configured a command to lint our code style. Now, we're going to integrate it with the pre-commit hook.&lt;/p&gt;
&lt;h4&gt;
  
  
  Put Linting Command to Pre-Commit hook
&lt;/h4&gt;

&lt;p&gt;Add &lt;code&gt;npm run lint&lt;/code&gt; to the first line of &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 diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+npm run lint
&lt;/span&gt;&lt;span class="p"&gt;npm test
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h4&gt;
  
  
  Try A Commit
&lt;/h4&gt;

&lt;p&gt;Now, all commits will trigger the execution of this linting command.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'second commit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You'll encounter a failure because you must fix all the linting errors (mentioned above) before committing the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzclz9pxevle4khhknju.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzclz9pxevle4khhknju.png" alt="Git Hook" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Fix the Linting Errors
&lt;/h4&gt;

&lt;p&gt;Fix it by editing &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// index.js
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+const process = {
+    env: {
+        bit: 2
+    }
+}
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const field = {
&lt;/span&gt;    "b": process.evn.bit,
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Commit again and it should work.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'commit after fix index.js'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw95egxjf44vic35rvllz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fw95egxjf44vic35rvllz.png" alt="Git Hook" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2.4. Brief Sum-Up
&lt;/h3&gt;

&lt;p&gt;By now, &lt;strong&gt;both &lt;code&gt;npm run lint&lt;/code&gt; and &lt;code&gt;npm test&lt;/code&gt; must pass in the &lt;code&gt;pre-commit&lt;/code&gt; hook before you can commit code&lt;/strong&gt;.&lt;/p&gt;
&lt;h4&gt;
  
  
  Better Linting
&lt;/h4&gt;

&lt;p&gt;I only made a very simple ESLint configuration here, which may be insufficient for a production project. To integrate a robust linting toolchain, you can check out another post of mine to learn more: &lt;a href="https://dev.to/graezykev/mastering-code-quality-setting-up-eslint-with-standard-js-in-typescript-projects-18gl"&gt;Mastering Code Quality: Setting Up ESLint with Standard JS in TypeScript Projects&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/graezykev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1425699%2F36981583-6544-43e8-8660-1842a1f5941d.jpeg" alt="graezykev"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/graezykev/mastering-code-quality-setting-up-eslint-with-standard-js-in-typescript-projects-18gl" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Mastering Code Quality: Setting Up ESLint with Standard JS in TypeScript Projects&lt;/h2&gt;
      &lt;h3&gt;Kev ・ May 6&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#codequality&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#standardjs&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#eslint&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#typescript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  3. lint-staged
&lt;/h2&gt;

&lt;p&gt;Have you noticed the problem with &lt;code&gt;npm run lint&lt;/code&gt; in the &lt;code&gt;pre-commit&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Yes, &lt;strong&gt;all your JavaScript files in the project&lt;/strong&gt; are checked in this process. What is the problem, though?&lt;/p&gt;

&lt;p&gt;Suppose you're working on a historical project with hundreds of JavaScript files that had never integrated linting tools or Git hooks before, meaning there may be numerous code style issues in the existing code.&lt;/p&gt;

&lt;p&gt;Today, you integrated ESLint and Git commit hooks into the project, and tomorrow your teammate edits just one JavaScript file. However, they can't commit it because they are overwhelmed by all the historical linting issues in the whole project at once, which are identified by &lt;code&gt;npm run lint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here’s another example:&lt;/p&gt;

&lt;p&gt;You've spent a day developing a web page and have written several files like &lt;code&gt;header.js&lt;/code&gt;, &lt;code&gt;aside.js&lt;/code&gt;, &lt;code&gt;main.js&lt;/code&gt;, &lt;code&gt;footer.js&lt;/code&gt;, etc. But, only &lt;code&gt;header.js&lt;/code&gt; is complete, the others are still under development.&lt;/p&gt;

&lt;p&gt;Now it's 5 o'clock, time to call it a day! You decide to commit &lt;code&gt;header.js&lt;/code&gt; first, but you encounter similar obstacles as in the previous example.&lt;/p&gt;

&lt;p&gt;What we need is a way to commit only the &lt;strong&gt;"code we really want to commit right now"&lt;/strong&gt;, or more correctly, by Git terminology, the &lt;strong&gt;staged&lt;/strong&gt; files.&lt;/p&gt;

&lt;p&gt;In simple terms, &lt;strong&gt;staged&lt;/strong&gt; files are those files you've added to the &lt;strong&gt;&lt;a href="https://git-scm.com/book/en/v2/Getting-Started-What-is-Git%3F#_the_three_states" rel="noopener noreferrer"&gt;Git Staging Area&lt;/a&gt;&lt;/strong&gt; by &lt;code&gt;git add &amp;lt;filename&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Only the code in the staging area should be linted with each commit.&lt;/p&gt;

&lt;p&gt;Now, let's see how to get it done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install lint-staged
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/lint-staged/lint-staged" rel="noopener noreferrer"&gt;lint-staged&lt;/a&gt; is the second important tools we need here, install it via NPM.&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;-D&lt;/span&gt; lint-staged
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure lint-staged
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;lint-staged.config.js&lt;/code&gt; file in the project root with the configuration below:&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;// lint-staged.config.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// You can lint other types of files with different tools.&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*.{js,jsx,ts,tsx}&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="c1"&gt;// You can also add other tools to lint your JavaScript here.&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eslint&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="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="c1"&gt;// I included this line to intentionally elicit an error output in ESLint later.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: I use &lt;code&gt;export default xxx&lt;/code&gt; here because my &lt;code&gt;package.json&lt;/code&gt; includes the configuration &lt;code&gt;"type": "module"&lt;/code&gt;. If you don't have this configuration, use &lt;code&gt;module.exports = xxx&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Add lint-staged to NPM Script
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;  "lint": "eslint .",
&lt;span class="gi"&gt;+ "lint:staged": "lint-staged",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And of course, modify your hook command accordingly in &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 diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-npm run lint
&lt;/span&gt;&lt;span class="gi"&gt;+npm run lint:staged
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use lint-staged
&lt;/h3&gt;

&lt;p&gt;Annotate the following code from &lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// index.js
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+// const process = {
+//    env: {
+//        bit: 2
+//    }
+// }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const field = {
&lt;/span&gt;    "b": process.evn.bit,
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this scenario, we have ESLint issues in both &lt;code&gt;lint-staged.config.js&lt;/code&gt; and &lt;code&gt;index.js&lt;/code&gt;, but let's say we don't want to commit &lt;code&gt;index.js&lt;/code&gt; and ignore its ESLint issues for now.&lt;/p&gt;

&lt;p&gt;To let &lt;code&gt;lint-staged&lt;/code&gt; identify what you're going to commit, first add the file you want to commit to the Git &lt;strong&gt;Staging Area&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;git add lint-staged.config.js &lt;span class="c"&gt;# don't add index.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, commit the file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'test lint-staged'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This time, only the &lt;strong&gt;newly added&lt;/strong&gt;(staged) file &lt;code&gt;lint-staged.config.js&lt;/code&gt; is checked during your commit.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt; was changed, and it obviously has ESLint errors, but it's not &lt;strong&gt;staged&lt;/strong&gt;, so it's not checked in this commit.&lt;/p&gt;

&lt;p&gt;You don't need to fix all the JavaScript files in the project, nor even all the JavaScript files you have modified, but just the &lt;strong&gt;staged&lt;/strong&gt; file(s) you actually want to commit.&lt;/p&gt;

&lt;p&gt;Let's comment out the line &lt;code&gt;var b&lt;/code&gt; in &lt;code&gt;lint-staged.config.js&lt;/code&gt; to fix its ESLint error, and then the commit will succeed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-var b // I included this line to intentionally elicit an error output in ESLint later.
&lt;/span&gt;&lt;span class="gi"&gt;+// var b // I included this line to intentionally elicit an error output in ESLint later.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add lint-staged.config.js &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'test lint-staged'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  lint-staged Other Files
&lt;/h3&gt;

&lt;p&gt;There are more linting tools that I won't go into deeply, but you can integrate them with &lt;code&gt;lint-staged&lt;/code&gt;. For example, you can lint your &lt;strong&gt;CSS&lt;/strong&gt; content with &lt;a href="https://stylelint.io/" rel="noopener noreferrer"&gt;Stylelint&lt;/a&gt;, or even lint your &lt;strong&gt;README&lt;/strong&gt; files with &lt;a href="https://github.com/DavidAnson/markdownlint" rel="noopener noreferrer"&gt;markdownlint&lt;/a&gt;, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Save other changes
&lt;/h3&gt;

&lt;p&gt;Now we have some unsaved work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Before we move on to our next Git Hook, let's revert our last change in &lt;code&gt;index.js&lt;/code&gt; and save our previous work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// index.js
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-// const process = {
-//    env: {
-//        bit: 2
-//    }
-// }
&lt;/span&gt;&lt;span class="gi"&gt;+const process = {
+    env: {
+        bit: 2
+    }
+}
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const field = {
&lt;/span&gt;    "b": process.evn.bit,
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"let's continue"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;We're going to mention this &lt;strong&gt;"let's continue"&lt;/strong&gt; later in our next Git Hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Commit Message Hook
&lt;/h2&gt;

&lt;p&gt;In the previous steps, we introduced the Git Pre-Commit Hook, which activates immediately after you execute the &lt;code&gt;git commit&lt;/code&gt; command but before the commit message editor opens or the commit is finalized.&lt;/p&gt;

&lt;p&gt;And after that, after the Git Pre-Commit Hook is triggered, the next hook is the Commit Message Hook, or specifically speaking, the &lt;code&gt;commit-msg&lt;/code&gt; script. In this hook, you're able to get the &lt;strong&gt;Commit Message&lt;/strong&gt; from the commit message editor or from the message you provide via &lt;code&gt;git commit -m 'message'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our next step is to validate the &lt;strong&gt;format&lt;/strong&gt; of the commit message.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why is format of the commit message so important?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, Git commit message is a &lt;strong&gt;semantic&lt;/strong&gt; description of what you are going to do in a specific commit. It's a gateway to &lt;strong&gt;communicate&lt;/strong&gt; with your teammates, code reviewers are easier to understand your intention without diving into the code.&lt;/p&gt;

&lt;p&gt;Git commit message also &lt;strong&gt;improves traceability&lt;/strong&gt;, by linking the commit to external resources like bug and task trackers through identifiers or tags, enhancing the traceability of work and the management of project documentation.&lt;/p&gt;

&lt;p&gt;That's the significance of adhering to a standard format for commit messages promotes clarity, coherence, and collaboration within a team.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1. Commit Message Format Linting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Install Commit Message Linting Tools
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://commitlint.js.org/" rel="noopener noreferrer"&gt;commitlint&lt;/a&gt; is the most important tool we need for this step.&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;--save-dev&lt;/span&gt; @commitlint/&lt;span class="o"&gt;{&lt;/span&gt;cli,config-conventional&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Configure &lt;code&gt;commitlint&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Below is a simple (but conventional) configuration, adhering to the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#summary" rel="noopener noreferrer"&gt;conventional commit format&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"export default { extends: ['@commitlint/config-conventional'] };"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; commitlint.config.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: I use &lt;code&gt;export default xxx&lt;/code&gt; here because my &lt;code&gt;package.json&lt;/code&gt; includes the configuration &lt;code&gt;"type": "module"&lt;/code&gt;. If you don't have this configuration, use &lt;code&gt;module.exports = xxx&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Test &lt;code&gt;commitlint&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Run &lt;code&gt;commitlint&lt;/code&gt; directly to verify the message we used in our last commit, checking if it meet the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#summary" rel="noopener noreferrer"&gt;conventional commit format&lt;/a&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 commitlint &lt;span class="nt"&gt;--from&lt;/span&gt; HEAD~1 &lt;span class="nt"&gt;--to&lt;/span&gt; HEAD &lt;span class="nt"&gt;--verbose&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;"from HEAD~1 to HEAD" is your &lt;strong&gt;latest commit&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You will encounter the error:&lt;/p&gt;

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

&lt;h4&gt;
  
  
  Why Dose It Fail?
&lt;/h4&gt;

&lt;p&gt;The command above is &lt;strong&gt;mimicking your last commit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Do you recall what it was? Yes, it was &lt;code&gt;git commit -m 'let's continue'&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this case, your &lt;strong&gt;commit message&lt;/strong&gt; is &lt;code&gt;"let's continue"&lt;/code&gt;, but we have a &lt;strong&gt;rule&lt;/strong&gt; for the commit message &lt;strong&gt;format&lt;/strong&gt; configured in &lt;code&gt;commitlint.config.js&lt;/code&gt;, which stipulates that the commit message should be structured as &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#summary" rel="noopener noreferrer"&gt;follows&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;description&amp;gt;

[optional body]

[optional footer(s)]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;i.e., your commit message must be at least formatted like &lt;code&gt;"feat: your commit description ..."&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;However, Your message of &lt;code&gt;"let's continue"&lt;/code&gt; doesn't satisfy the rule, which means your commit will fail.&lt;/p&gt;

&lt;p&gt;We'll find out how to fix it later.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2. Add Message Format Linting to Commit Message Hook
&lt;/h3&gt;

&lt;p&gt;Running &lt;code&gt;commitlint&lt;/code&gt; directly is not appropriate; we should integrate it into an automatic Git Hook (Commit Message Hook).&lt;/p&gt;

&lt;h4&gt;
  
  
  Add Linting Script to the Hook
&lt;/h4&gt;

&lt;p&gt;Create the Git Commit Message Hook file &lt;code&gt;.husky/commit-msg&lt;/code&gt; and add execution of &lt;code&gt;commitlint&lt;/code&gt; to it.&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"npx --no -- commitlint --edit &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;1"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .husky/commit-msg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see a newly created file &lt;code&gt;.husky/commit-msg&lt;/code&gt; with the content below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gi"&gt;+npx --no -- commitlint --edit \$1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Test the Hook
&lt;/h4&gt;

&lt;p&gt;The Commit Message Hook is ready, now test it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"this will fail"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fails because neither a &lt;code&gt;type&lt;/code&gt; nor a &lt;code&gt;subject&lt;/code&gt; is specified.&lt;/p&gt;

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

&lt;p&gt;Make some slightly adjustments:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"foo: this will also fail"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As &lt;code&gt;foo&lt;/code&gt; is not a valid &lt;code&gt;type&lt;/code&gt;, it fails again, But at least we have &lt;strong&gt;one less problem&lt;/strong&gt; than the last commit.&lt;/p&gt;

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

&lt;p&gt;Modify the message once more:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"chore: this is a legal commit message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hooray!&lt;/p&gt;

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

&lt;h3&gt;
  
  
  4.3. Tailor your Commit Message Format
&lt;/h3&gt;

&lt;p&gt;In real senarios, the &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/#summary" rel="noopener noreferrer"&gt;conventional commit message format&lt;/a&gt; may not meet your team's requirements, sometimes you need to customise your rules.&lt;/p&gt;

&lt;p&gt;For instance, your team is using &lt;a href="https://www.atlassian.com/software/jira" rel="noopener noreferrer"&gt;Jira&lt;/a&gt; for project and product management, as well as issue tracking, etc. You and your teammates have agreed that every commit should include a Jira ticket ID, allowing you to trace back the real motivation (a product requirement, a technical optimization, a bug, etc.) of every code change.&lt;/p&gt;

&lt;p&gt;To do this, edit your &lt;code&gt;commitlint.config.js&lt;/code&gt; as follows:&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;// commitlint.config.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&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;@commitlint/config-conventional&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;plugins&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="na"&gt;rules&lt;/span&gt;&lt;span class="p"&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;subject-prefix-with-jira-ticket-id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;parsed&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;parsed&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\[[&lt;/span&gt;&lt;span class="sr"&gt;A-Z&lt;/span&gt;&lt;span class="se"&gt;]{3,5}&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;\]\s&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;match&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="kc"&gt;true&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s2"&gt;`The commit message's subject must be prefixed with an uppercase JIRA ticket ID.
    A correct commit message should be like: feat: [JIRA-1234] fulfill this feature
    Your subject: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
    Please revise your commit message.
    `&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="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&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;subject-prefix-with-jira-ticket-id&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;always&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;blockquote&gt;
&lt;p&gt;Note: I use &lt;code&gt;export default xxx&lt;/code&gt; here because my &lt;code&gt;package.json&lt;/code&gt; includes the configuration &lt;code&gt;"type": "module"&lt;/code&gt;. If you don't have this configuration, use &lt;code&gt;module.exports = xxx&lt;/code&gt; instead.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'chore: try to commit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oops! The commit fails as we just add a new rule to force a &lt;strong&gt;"JIRA ticket ID"&lt;/strong&gt; in the commit message's subject.&lt;/p&gt;

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

&lt;p&gt;Try another one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'chore: [PRJ-1234] a commit with sample id'&lt;/span&gt; &lt;span class="c"&gt;# the [PRJ-1234] is a fake ID&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You've got it!&lt;/p&gt;

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

&lt;h2&gt;
  
  
  5. Git Push Hook
&lt;/h2&gt;

&lt;p&gt;We have taken some steps to ensure our code style and commit message format in the previous steps. One further step is to ensure the code quality before it's pushed to the remote repository.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Git Push Hook&lt;/strong&gt; is triggered before a push (&lt;code&gt;git push origin &amp;lt;branch&amp;gt;&lt;/code&gt;) occurs. You can use the Git Push Hook as another &lt;strong&gt;"firewall"&lt;/strong&gt; to validate the code before it is pushed to the remote repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1. Incremental Code Linting
&lt;/h3&gt;

&lt;p&gt;We have run &lt;code&gt;npm run lint:staged&lt;/code&gt; in &lt;code&gt;pre-commit&lt;/code&gt;. Does it ensure that there will be no unchecked code contaminating our codebase?&lt;/p&gt;

&lt;p&gt;The answer is &lt;strong&gt;No&lt;/strong&gt;. You can still commit code that hasn't been fixed by this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'whatever I like'&lt;/span&gt; &lt;span class="nt"&gt;--no-verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the &lt;code&gt;--no-verify&lt;/code&gt; flag? It can help you to &lt;strong&gt;skip &lt;code&gt;pre-commit&lt;/code&gt; and &lt;code&gt;commit-msg&lt;/code&gt; hooks&lt;/strong&gt; we made above.&lt;/p&gt;

&lt;p&gt;This allows for a &lt;strong&gt;forced commit&lt;/strong&gt;. It's like a hidden time bomb! You're not even able to stop your teammates from doing it sneakily!&lt;/p&gt;

&lt;p&gt;So, here we need a second defence line before the &lt;strong&gt;forced committed&lt;/strong&gt; code is pushed to our remote repository and pollute the codebase.&lt;/p&gt;

&lt;p&gt;I need to clarify what I mean by &lt;strong&gt;Incremental Code&lt;/strong&gt; here.&lt;/p&gt;

&lt;p&gt;Say we have the following commits: &lt;code&gt;commit-a&lt;/code&gt; and &lt;code&gt;commit-b&lt;/code&gt; have been pushed to the remote repository, and &lt;code&gt;commit-c&lt;/code&gt; to &lt;code&gt;commit-N&lt;/code&gt; (not sure how many commits in &lt;code&gt;...&lt;/code&gt;) have not been pushed yet.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Commit&lt;/th&gt;
&lt;th&gt;Commit time&lt;/th&gt;
&lt;th&gt;push status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;commit-N&lt;/td&gt;
&lt;td&gt;today&lt;/td&gt;
&lt;td&gt;not pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;...&lt;/td&gt;
&lt;td&gt;today&lt;/td&gt;
&lt;td&gt;not pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;commit-d&lt;/td&gt;
&lt;td&gt;today&lt;/td&gt;
&lt;td&gt;not pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;commit-c&lt;/td&gt;
&lt;td&gt;today&lt;/td&gt;
&lt;td&gt;not pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;commit-b&lt;/td&gt;
&lt;td&gt;yesterday&lt;/td&gt;
&lt;td&gt;pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;commit-a&lt;/td&gt;
&lt;td&gt;the-day-before-yesterday&lt;/td&gt;
&lt;td&gt;pushed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Right now, we're going to push from &lt;code&gt;commit-c&lt;/code&gt; to &lt;code&gt;commit-N&lt;/code&gt;, and &lt;code&gt;commit-c&lt;/code&gt; to &lt;code&gt;commit-N&lt;/code&gt; represent the &lt;strong&gt;incremental code&lt;/strong&gt; in this context.&lt;/p&gt;

&lt;h4&gt;
  
  
  First Push
&lt;/h4&gt;

&lt;p&gt;Before making any changes and testing the incremental changes since the last push, it's &lt;strong&gt;important&lt;/strong&gt; to &lt;strong&gt;push the code&lt;/strong&gt; first.&lt;/p&gt;

&lt;p&gt;Because later, we're going to make another push, which will be compared to this push as &lt;strong&gt;Incremental Code&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;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h4&gt;
  
  
  Incremental Code Linting Shell Script
&lt;/h4&gt;

&lt;p&gt;Create a &lt;strong&gt;Shell script&lt;/strong&gt; file named &lt;code&gt;scripts/lint-incremental-push-files.sh&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;scripts &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;scripts/lint-incremental-push-files.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this Shell script, we'll identify those &lt;strong&gt;incremental&lt;/strong&gt; JavaScript files we want to &lt;strong&gt;push&lt;/strong&gt;, and then run the ESLint command only on them. Input the following code into &lt;code&gt;lint-incremental-push-files.sh&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/bash&lt;/span&gt;

&lt;span class="c"&gt;# Ensure you have the latest info from your remote&lt;/span&gt;
git fetch

&lt;span class="c"&gt;# Automatically identify the current branch and corresponding remote branch&lt;/span&gt;
&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="nt"&gt;--abbrev-ref&lt;/span&gt; HEAD&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;REMOTE_BRANCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"origin/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BRANCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Find the last commit from the remote branch that has been pushed&lt;/span&gt;
&lt;span class="nv"&gt;LAST_PUSHED_COMMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REMOTE_BRANCH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# Find the current commit&lt;/span&gt;
&lt;span class="nv"&gt;CURRENT_COMMIT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse HEAD&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# List changed files since the last pushed commit that match the desired extensions&lt;/span&gt;
&lt;span class="nv"&gt;CHANGED_FILES&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nv"&gt;$LAST_PUSHED_COMMIT&lt;/span&gt; &lt;span class="nv"&gt;$CURRENT_COMMIT&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'\.(js|jsx|ts|tsx)$'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Files to lint:"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$CHANGED_FILES&lt;/span&gt;

&lt;span class="c"&gt;# Run ESLint on these files if any are found&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CHANGED_FILES&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"No JavaScript/TypeScript files to lint."&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Linting files..."&lt;/span&gt;
    ./node_modules/.bin/eslint &lt;span class="nv"&gt;$CHANGED_FILES&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Linting issues found, please fix them."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;1
    &lt;span class="k"&gt;fi
fi&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the script executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x scripts/lint-incremental-push-files.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this script to a NPM script in &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 diff"&gt;&lt;code&gt;  "scripts": {
    "prepare": "husky",
    "lint": "eslint .",
    "lint:staged": "lint-staged",
&lt;span class="gi"&gt;+   "lint:incremental-push": "./scripts/lint-incremental-push-files.sh",
&lt;/span&gt;    "test": "exit 0"
  },
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the &lt;strong&gt;Git Push Hook&lt;/strong&gt; configuration file named &lt;code&gt;.husky/pre-push&lt;/code&gt;, and add the NPM script to it.&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"npm run lint:incremental-push"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .husky/pre-push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, this Shell script will run every time before your push. Your &lt;code&gt;git push&lt;/code&gt; command will fail if the incremental code doesn't pass the ESLint check, meaning no &lt;strong&gt;force-committed&lt;/strong&gt; code can pass through!&lt;/p&gt;

&lt;h4&gt;
  
  
  Test Incremental Code Linting
&lt;/h4&gt;

&lt;p&gt;Now, let's make some changes.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;index.js&lt;/code&gt; to add a simple line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;// index.js
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const process = {
&lt;/span&gt;    env: {
        bit: 2
    }
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export const field = {
&lt;/span&gt;    "b": process.evn.bit,
}
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+var a
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You &lt;strong&gt;can not&lt;/strong&gt; commit it because we have a &lt;code&gt;pre-commit&lt;/code&gt; hook to lint the file and we just made a ESLint error in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'bypass eslint to commit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;But you can bypass the check with &lt;code&gt;--no-verify&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;git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'bypass eslint to commit'&lt;/span&gt; &lt;span class="nt"&gt;--no-verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Do a similar thing to &lt;code&gt;eslint.config.js&lt;/code&gt; with a new line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;import pluginJs from "@eslint/js";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;export default [
&lt;/span&gt;  pluginJs.configs.recommended
];
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+var b;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bypass the check process again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'bypass eslint again to commit'&lt;/span&gt; &lt;span class="nt"&gt;--no-verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Check and Push Incremental Code
&lt;/h4&gt;

&lt;p&gt;Just now, we have two commits including &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;eslint.config.js&lt;/code&gt;, in which there are actually ESLint issues, but they were committed using tricks (&lt;code&gt;--no-verify&lt;/code&gt;).&lt;/p&gt;

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

&lt;p&gt;The commits in the red rectangle are the so-called &lt;strong&gt;incremental changes&lt;/strong&gt; to be pushed, but they should not be pushed since they have ESLint issues!&lt;/p&gt;

&lt;p&gt;But don't panic, we've got your back! They won't be able to be pushed because they will face the punishment of the &lt;strong&gt;Git Push Hook&lt;/strong&gt; we made above!&lt;/p&gt;

&lt;p&gt;If you try to push the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All &lt;strong&gt;Incremental Errors&lt;/strong&gt; will be caught!&lt;/p&gt;

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

&lt;p&gt;You can not push them until fix the errors and commit again.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;index.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-var a
&lt;/span&gt;&lt;span class="gi"&gt;+// var a
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;eslint.config.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-var b;
&lt;/span&gt;&lt;span class="gi"&gt;+// var b;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'fix: [TEST-01] fix ESLint errors'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h3&gt;
  
  
  5.2. Force &lt;code&gt;test&lt;/code&gt; Before Push
&lt;/h3&gt;

&lt;p&gt;Next, I'm going to modify our team's workflow.&lt;/p&gt;

&lt;p&gt;I will move the &lt;code&gt;npm test&lt;/code&gt; command from the &lt;strong&gt;Git  Pre-Commit Hook&lt;/strong&gt; hook to the &lt;strong&gt;Git Push Hook&lt;/strong&gt; named &lt;code&gt;pre-push&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is because I decide to allow &lt;strong&gt;linted&lt;/strong&gt; code &lt;strong&gt;commits&lt;/strong&gt;, without the &lt;code&gt;test&lt;/code&gt; verification. I mean, we allow &lt;strong&gt;"un-test"&lt;/strong&gt; code to be commit, but don't allow them to be pushed.&lt;/p&gt;

&lt;p&gt;To do that, 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 diff"&gt;&lt;code&gt;&lt;span class="p"&gt;npm run lint:staged
&lt;/span&gt;&lt;span class="gd"&gt;- npm test
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And move the &lt;code&gt;test&lt;/code&gt; command to the &lt;strong&gt;Git Push Hook&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"npm test"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .husky/pre-push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's edit the &lt;code&gt;package.json&lt;/code&gt;'s &lt;code&gt;test&lt;/code&gt; command to intentionally make the test fail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;"scripts": {
&lt;span class="gd"&gt;-  "test": "exit 0",
&lt;/span&gt;&lt;span class="gi"&gt;+  "test": "exit 1",
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if you try to push any code, you'll fail because we have an &lt;code&gt;exit 1&lt;/code&gt; in the command. This means the &lt;code&gt;test&lt;/code&gt; process is not passed, preventing you from pushing the &lt;strong&gt;"un-tested"&lt;/strong&gt; code to the remote repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s1"&gt;'chore: [TEST-1234] test commit'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;This would have failed if we hadn't removed &lt;code&gt;npm test&lt;/code&gt; from &lt;code&gt;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;git push origin main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The push will fail because we just added a &lt;code&gt;test&lt;/code&gt; in the &lt;code&gt;pre-push&lt;/code&gt; hook, which means we need to ensure that the &lt;code&gt;test&lt;/code&gt; succeeds before we can push our code.&lt;/p&gt;

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

&lt;p&gt;Revert &lt;code&gt;exit 1&lt;/code&gt; to &lt;code&gt;exit 0&lt;/code&gt;, or in a real production project, use your &lt;strong&gt;actual test scripts&lt;/strong&gt; that can pass, your code push to the remote repository will succeed!&lt;/p&gt;

&lt;h3&gt;
  
  
  5.3 Force Push
&lt;/h3&gt;

&lt;p&gt;Unfortunately, you can still bypass the &lt;code&gt;pre-push&lt;/code&gt; check and force push the code by using the &lt;code&gt;--no-verify&lt;/code&gt; flag, just as you can force a commit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push origin main &lt;span class="nt"&gt;--no-verify&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you're using GitHub, you can even commit and push your code directly on the website.&lt;/p&gt;

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

&lt;p&gt;To address this, we need &lt;strong&gt;server-side&lt;/strong&gt; Git hooks or &lt;code&gt;CI&lt;/code&gt; systems. However, these are more complex topics, and I won't delve deeply into them now. Perhaps I'll write another post to introduce them in the future.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: DIY your Workflows
&lt;/h2&gt;

&lt;p&gt;In this post, I've delved into detailed steps to normalise our teams' Git workflow. My approach is just a basic framework; there are many Git hooks I haven't mentioned here. You should customize your Git hooks to tailor them to your team's workflow.&lt;/p&gt;

&lt;p&gt;For example, you may not agree with me that I move the &lt;code&gt;npm test&lt;/code&gt; command from the &lt;strong&gt;Git Pre-Commit Hook&lt;/strong&gt; to the &lt;strong&gt;Git Push Hook&lt;/strong&gt;. Based on this post, I think you already know how to make your own choice and tailor your own workflows.&lt;/p&gt;

&lt;p&gt;Here are some additional steps you might consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make use of the other Git Hooks I didn't introduce in this post to enhance your teamwork, such as &lt;code&gt;post-checkout&lt;/code&gt;, &lt;code&gt;post-commit&lt;/code&gt;, &lt;code&gt;pre-rebase&lt;/code&gt;, etc.&lt;/li&gt;
&lt;li&gt;Implement specific rules for commit message formatting after discussing with your teammates.&lt;/li&gt;
&lt;li&gt;Perform both linting and testing in commits and pushes.&lt;/li&gt;
&lt;li&gt;Add other commands or scripts to your Git commit/push hooks, such as sending an IM message or an email to notify your teammates of your changes.&lt;/li&gt;
&lt;li&gt;Use your imagination to customize the workflow as you see fit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Last but not least, any suggestions, corrections, questions, or disagreements will be greatly appreciated.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>githook</category>
      <category>husky</category>
      <category>commitlint</category>
      <category>lintstaged</category>
    </item>
    <item>
      <title>Mastering Code Quality: Setting Up ESLint with Standard JS in TypeScript Projects</title>
      <dc:creator>Kev</dc:creator>
      <pubDate>Mon, 06 May 2024 02:55:00 +0000</pubDate>
      <link>https://dev.to/graezykev/mastering-code-quality-setting-up-eslint-with-standard-js-in-typescript-projects-18gl</link>
      <guid>https://dev.to/graezykev/mastering-code-quality-setting-up-eslint-with-standard-js-in-typescript-projects-18gl</guid>
      <description>&lt;p&gt;Learn how to enhance your TypeScript project by integrating &lt;a href="https://standardjs.com/rules" rel="noopener noreferrer"&gt;JavaScript Standard Style&lt;/a&gt; rules using &lt;a href="https://github.com/mightyiam/eslint-config-love" rel="noopener noreferrer"&gt;&lt;code&gt;eslint-config-love&lt;/code&gt;&lt;/a&gt; in ESLint. This guide will also provide you with essential tips for setting up &lt;strong&gt;automatic formatting&lt;/strong&gt; in Visual Studio Code, ensuring your code remains clean and consistent. Furthermore, we'll explore the inclusion of ESLint plugins specifically designed for &lt;strong&gt;React&lt;/strong&gt; and React Hooks to optimise your development workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Configuring ESLint in a TypeScript project is nothing much, the more challenging aspect is choosing a &lt;strong&gt;Code Style&lt;/strong&gt; for your team.&lt;/p&gt;

&lt;p&gt;You may be torn between those famous code styles, struggling to choose one between &lt;a href="https://github.com/airbnb/javascript" rel="noopener noreferrer"&gt;Airbnb JavaScript Style&lt;/a&gt;, &lt;a href="https://google.github.io/styleguide/jsguide.html" rel="noopener noreferrer"&gt;Google JavaScript Style Guide&lt;/a&gt;, &lt;a href="https://standardjs.com/rules" rel="noopener noreferrer"&gt;JavaScript Standard Style&lt;/a&gt;, or &lt;a href="https://github.com/xojs/xo" rel="noopener noreferrer"&gt;XO&lt;/a&gt;, among others.&lt;/p&gt;

&lt;p&gt;What's worse, you and your teammates may be arguing about whether you should use &lt;code&gt;;&lt;/code&gt; at the end of each line, whether &lt;code&gt;if() {&lt;/code&gt; should have a space after &lt;code&gt;if&lt;/code&gt;, whether &lt;code&gt;function foo () {return true}&lt;/code&gt; is a bad way and should change it to &lt;code&gt;function foo () { return true }&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;In my personal perspective, it doesn't matter which style you choose, but you need a &lt;strong&gt;unified and strict&lt;/strong&gt; style across a team. Once the rules are set, there's no room for argument, as long as the rules are detailed and cover most aspects.&lt;/p&gt;

&lt;p&gt;So, self-assertively, I have chosen &lt;a href="https://standardjs.com/rules" rel="noopener noreferrer"&gt;JavaScript Standard Style&lt;/a&gt;, also known as Standard JS, even though it is not at all standard—the name is somewhat misleading.&lt;/p&gt;

&lt;p&gt;Standard JS is &lt;strong&gt;simple, clear, straightforward, and detailed&lt;/strong&gt;, and I think sticking to the rules makes JS/TS code very &lt;strong&gt;clean&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It doesn't allow configurations&lt;/strong&gt;, &lt;strong&gt;rules are rules&lt;/strong&gt;. No configurations, which is by design to avoid too much bikeshedding over style choices, So I don't need to argue with my teammates.&lt;/p&gt;

&lt;p&gt;Examples.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// ✓ ok&lt;/span&gt;
&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// ✗ avoid&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&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;hello there&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// ✓ ok&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="s2"&gt;hello there&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// ✗ avoid&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="s2"&gt;`hello there`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;// ✗ avoid&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="cm"&gt;/* comment */&lt;/span&gt; &lt;span class="c1"&gt;// ✓ ok&lt;/span&gt;
&lt;span class="cm"&gt;/*comment*/&lt;/span&gt;   &lt;span class="c1"&gt;// ✗ avoid&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;I have to admit that Standard JS is &lt;strong&gt;opinionated&lt;/strong&gt;, and choosing it is a subjective thing. Some people even hate it—we're all right.&lt;/p&gt;

&lt;p&gt;In this post, I also use ESLint + Standard JS as my &lt;strong&gt;code formatting&lt;/strong&gt; tools. Formatting JS/TS code by using ESLint is also subjective and opinionated, arguably most people would rather use &lt;a href="https://prettier.io/" rel="noopener noreferrer"&gt;Prettier&lt;/a&gt; instead, which provides more configurable options.&lt;/p&gt;

&lt;p&gt;But like I said before, Standard JS's philosophy is "rules are rules", its rules are detailed, some arguable rules are strictly normalised and no compromises are allowed, customising Prettier's options will lead me back to endless arguments with my teammates.&lt;/p&gt;

&lt;p&gt;Sorry, I've gone too far. I'm not here to persuade you to use Standard JS. My intention is to provide information and guidance on configuring &lt;a href="https://standardjs.com/" rel="noopener noreferrer"&gt;JavaScript Standard Style&lt;/a&gt; for your team, should you agree with me or have other reasons to choose it.&lt;/p&gt;
&lt;h3&gt;
  
  
  GitHub Repo
&lt;/h3&gt;

&lt;p&gt;If you'd prefer to run the demo I've created instead of following the steps individually, check out this &lt;a href="https://github.com/graezykev/ts-eslint-standard-js" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for a quick overview and hands-on experience.&lt;/p&gt;
&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;p&gt;I primarily divide this post into 5 parts.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Initial Setup&lt;/li&gt;
&lt;li&gt;ESLint Configuration&lt;/li&gt;
&lt;li&gt;Editor (VS Code) Integration&lt;/li&gt;
&lt;li&gt;Automate Linting and Formatting&lt;/li&gt;
&lt;li&gt;Linting React &amp;amp; React Hooks&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  1. Initial Setup
&lt;/h2&gt;

&lt;p&gt;If you're not setting up a new TypeScript project, you can skip this part 1.&lt;/p&gt;
&lt;h3&gt;
  
  
  Init Git
&lt;/h3&gt;

&lt;p&gt;Initialize a Git repository if you're starting a new project.&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;mkdir &lt;/span&gt;ts-eslint-standard-js &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;ts-eslint-standard-js &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
git init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'node_modules'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; .gitignore


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Init Project
&lt;/h3&gt;

&lt;p&gt;Initialize your NPM package.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm init &lt;span class="nt"&gt;-y&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;I recommend enabling ES6 modules in your project, indicating that your source code should use &lt;strong&gt;import&lt;/strong&gt; syntax.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm pkg &lt;span class="nb"&gt;set type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;I also recommend upgrading your Node.js version and specifying the minimum version requirements in your project.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm pkg &lt;span class="nb"&gt;set &lt;/span&gt;engines.node&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=18.18.0 &amp;lt;19 || &amp;gt;=20.9.0 &amp;lt;21 || &amp;gt;=21.1.0"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Install TypeScript
&lt;/h3&gt;

&lt;p&gt;Run this in your terminal to install TypeScript.&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;-D&lt;/span&gt; typescript


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Init TypeScript
&lt;/h3&gt;

&lt;p&gt;Use the following command to initialise a new TypeScript project.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx tsc &lt;span class="nt"&gt;--init&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;A &lt;code&gt;tsconfig.json&lt;/code&gt; will be created under your project.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. ESLint Configuration
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Install ESLint
&lt;/h3&gt;

&lt;p&gt;Run the command below to install &lt;strong&gt;ESLint&lt;/strong&gt; packages.&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;-D&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  eslint@^8.57.0 &lt;span class="se"&gt;\&lt;/span&gt;
  typescript-eslint@^7.6.0 &lt;span class="se"&gt;\&lt;/span&gt;
  eslint-plugin-promise@^6.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  eslint-plugin-import@^2.25.2 &lt;span class="se"&gt;\&lt;/span&gt;
  eslint-plugin-n@^15.0.0 &lt;span class="se"&gt;\&lt;/span&gt;
  @typescript-eslint/eslint-plugin@^7.0.1 &lt;span class="se"&gt;\&lt;/span&gt;
  eslint-config-love@latest &lt;span class="se"&gt;\&lt;/span&gt;
  globals@^15.0.0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: These versions can be changed in the future.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The packages installed include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESLint Core&lt;/li&gt;
&lt;li&gt;ESLint shared configs and plugins for &lt;strong&gt;Standard JS&lt;/strong&gt;, with the most important one being &lt;code&gt;eslint-config-love&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  ESLint configuration
&lt;/h3&gt;

&lt;p&gt;Create a file named &lt;code&gt;eslint.config.js&lt;/code&gt; with the code below:&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;globals&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;globals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;tseslint&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;typescript-eslint&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&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;path&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;fileURLToPath&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="s1"&gt;url&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;FlatCompat&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="s1"&gt;@eslint/eslintrc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;pluginJs&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;@eslint/js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// mimic CommonJS variables -- not needed if using CommonJS&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;__filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fileURLToPath&lt;/span&gt;&lt;span class="p"&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;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&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;__dirname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&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;compat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FlatCompat&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baseDirectory&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;recommendedConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pluginJs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recommended&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&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;**/*.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;languageOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sourceType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&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="na"&gt;languageOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;browser&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;tseslint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recommended&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;compat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;love&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;I use &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; here because in Part 1 I set the package type to &lt;code&gt;module&lt;/code&gt; to enable ES6 modules. If your project does not use ES6 modules, you may name this file as &lt;code&gt;eslint.config.mjs&lt;/code&gt; or modify the &lt;code&gt;import&lt;/code&gt; and &lt;code&gt;export&lt;/code&gt; syntax to use &lt;code&gt;CommonJS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are only 2 lines you need to notice now.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;tseslint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;recommended&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;compat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;love&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The first line instructs ESLint to parse and check TypeScript syntax, while the second line enforces adherence to &lt;a href="https://standardjs.com/rules" rel="noopener noreferrer"&gt;Standard JS rules&lt;/a&gt; for both JavaScript and TypeScript code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Try Out
&lt;/h3&gt;

&lt;p&gt;Run this command to verify the configuration file itself:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint eslint.config.js


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Yes, there are indeed some errors in the configuration file.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fzxsttobgujb7dw8zjx5e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fzxsttobgujb7dw8zjx5e.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Try &lt;code&gt;.ts&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create an &lt;code&gt;index.ts&lt;/code&gt; with the code:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And check its code style:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint index.ts


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Showing the errors indicates that the configurations also work for TypeScript!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fwgydslqstzn0cmcyvg0b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fwgydslqstzn0cmcyvg0b.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Besides, you can use &lt;code&gt;npx eslint .&lt;/code&gt; to check the style of all your files with one command.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fwmchjy6z2ctfjmot3s5b.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fwmchjy6z2ctfjmot3s5b.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Fix Code Style Issues
&lt;/h3&gt;

&lt;p&gt;You can use the &lt;code&gt;--fix&lt;/code&gt; flag in ESLint to automatically correct some of the illegal syntax and style issues in your code.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint &lt;span class="nt"&gt;--fix&lt;/span&gt; index.ts &lt;span class="c"&gt;# npx eslint --fix .&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Some issues will be automatically fixed, such as changing &lt;code&gt;"&lt;/code&gt; to &lt;code&gt;'&lt;/code&gt;, removing unnecessary semicolons at the end of lines, and so on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F3k16kqjdzbbrcyse4vte.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F3k16kqjdzbbrcyse4vte.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Certainly, there's still &lt;strong&gt;1 problem left&lt;/strong&gt;, as not all issues can be automatically resolved. For instance, ESLint typically won't fix &lt;strong&gt;unused variables&lt;/strong&gt;; you'll need to address those manually.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Editor (VS Code) Integration
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;npx eslint .&lt;/code&gt; and &lt;code&gt;npx eslint --fix .&lt;/code&gt; to check and format every JS/TS file in your codebase can be a nightmare, which is laborious and sometimes uncontrollable.&lt;/p&gt;

&lt;p&gt;Imagine you need to edit the code in the editor and run the commands in the terminal, you may be facing a huge amount of errors at the time you run &lt;code&gt;npx eslint&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A way to ease the anxiety is by integrating with the &lt;strong&gt;ESLint VS Code Extension&lt;/strong&gt;. It checks and formats the code problems along with your coding. For example, if you make a mistake, you'll be alerted by the editor immediately and can fix it right away.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You may not be using VS Code, but other editors like WebStorm offer similar solutions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  VS Code ESLint Extension
&lt;/h3&gt;

&lt;p&gt;Search for &lt;code&gt;dbaeumer.vscode-eslint&lt;/code&gt; in the Extensions panel and install it.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;At the time I write this, I'm using version 3.0.5 (pre-release) of this extension. Other versions may have some unknown issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;First, create the configuration file.&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;mkdir&lt;/span&gt; .vscode &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; .vscode/settings.json


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Edit &lt;code&gt;.vscode/settings.json&lt;/code&gt; to enable the ESLint extension in your editor.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&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;"eslint.enable"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Reloading your VS Code window is required. You'll see errors detected by the ESLint extension showing up in the editor while you are editing the code. These errors will be highlighted with &lt;strong&gt;wavy lines&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fq404oham0a61nnmspuef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fq404oham0a61nnmspuef.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hovering over each wavy line will toggle a pop-up displaying the error details.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fxouxpnfs9hnnvxcelc5o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fxouxpnfs9hnnvxcelc5o.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Auto Format on Save
&lt;/h3&gt;

&lt;p&gt;This VS Code ESLint extension can also automatically fix your code's linting issues. It operates similarly to running &lt;code&gt;npx eslint&lt;/code&gt; on the file while you're editing it.&lt;/p&gt;

&lt;p&gt;Modify &lt;code&gt;.vscode/settings.json&lt;/code&gt; with some additional configurations:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"eslint.format.enable"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.formatOnSave"&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="err"&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;"editor.defaultFormatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumer.vscode-eslint"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"[typescript]"&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;"editor.defaultFormatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumer.vscode-eslint"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"[javascriptreact]"&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;"editor.defaultFormatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumer.vscode-eslint"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"[typescriptreact]"&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;"editor.defaultFormatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dbaeumer.vscode-eslint"&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;Now, every time you type some code and press &lt;code&gt;Command + S&lt;/code&gt; to save them, the problems in the file that can be automatically fixed will be corrected automatically, just looks like your are running &lt;code&gt;eslint --fix eslint.config.js&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fyp00nd3v1153hdh0gbko.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fyp00nd3v1153hdh0gbko.gif" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The semicolons will disappear after you press &lt;code&gt;Command + S&lt;/code&gt; to save your code.&lt;/p&gt;

&lt;p&gt;By sharing this &lt;code&gt;settings.json&lt;/code&gt; in Git or any other version control tools, if your teammates have the extension &lt;code&gt;dbaeumer.vscode-eslint&lt;/code&gt; installed, the same behaviour will occur in their VS Code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Recommend Installing the Extension
&lt;/h3&gt;

&lt;p&gt;You can recommend your teammates to install &lt;code&gt;dbaeumer.vscode-eslint&lt;/code&gt; via the following configure.&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;touch&lt;/span&gt; .vscode/extensions.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{   
  "recommendations": ["dbaeumer.vscode-eslint"]
}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .vscode/extensions.json


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;VS Code prompts your teammates to install &lt;code&gt;dbaeumer.vscode-eslint&lt;/code&gt; when the project is opened for the first time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fiwsbdmadsxyem2o5iado.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fiwsbdmadsxyem2o5iado.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  devcontainer
&lt;/h3&gt;

&lt;p&gt;If your team is using a &lt;strong&gt;Cloud Development Environment&lt;/strong&gt; such as &lt;a href="https://github.com/features/codespaces" rel="noopener noreferrer"&gt;GitHub Codespaces&lt;/a&gt;, or &lt;a href="https://code.visualstudio.com/docs/devcontainers/tutorial" rel="noopener noreferrer"&gt;Dev Containers&lt;/a&gt; such as Docker, you can even share the installation of &lt;code&gt;dbaeumer.vscode-eslint&lt;/code&gt; with your teammates, via &lt;code&gt;devcontainer.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When someone opens the project in VS Code, the specified extension (as well as the specific version) &lt;code&gt;dbaeumer.vscode-eslint@3.0.5&lt;/code&gt; will be automatically installed within the devcontainer.&lt;/p&gt;

&lt;p&gt;The automatic installation only applies to the devcontainer environment.&lt;/p&gt;

&lt;p&gt;I love this, but it's a larger topic, and I won't elaborate too much on it here, you can just give it a try.&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;mkdir&lt;/span&gt; .devcontainer &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;touch&lt;/span&gt; .devcontainer/devcontainer.json &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'{
  "name": "ts-eslint-standard-js",
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint@3.0.5"
      ]
    }
  }
}'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .devcontainer/devcontainer.json


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The extension &lt;code&gt;dbaeumer.vscode-eslint&lt;/code&gt; will be automatically installed if you try it from &lt;a href="https://github.com/graezykev/ts-eslint-standard-js" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fyfm0h3j877dqfjq6jopl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fyfm0h3j877dqfjq6jopl.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Extra Tricks
&lt;/h3&gt;

&lt;p&gt;Different developers have their own default editor behaviours.&lt;/p&gt;

&lt;p&gt;For instance, in your VS Code, pressing the &lt;code&gt;Tab&lt;/code&gt; key may produce a tab with a width of 4 spaces, while for your teammates, it may produce 2 spaces or 4 spaces, depending on their default settings.&lt;/p&gt;

&lt;p&gt;Under the rules of &lt;a href="https://standardjs.com/rules" rel="noopener noreferrer"&gt;Standard JS&lt;/a&gt;, the use of real &lt;strong&gt;tabs&lt;/strong&gt; for code indentation is not allowed; instead, you have to use 2 spaces.&lt;/p&gt;

&lt;p&gt;To collaborate better with your teammates or enforce the rules of our code style more strictly, I recommend configuring the VS Code editor and sharing the configurations to unify this behaviour. For example, you can set it to automatically insert 2 spaces after pressing the &lt;code&gt;Tab&lt;/code&gt; key instead of inserting a real &lt;strong&gt;tab&lt;/strong&gt;, following the rule defined by &lt;a href="https://standardjs.com/rules" rel="noopener noreferrer"&gt;Standard JS&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To achieve this, add the following 3 configurations to &lt;code&gt;.vscode/settings.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="nl"&gt;"editor.tabSize"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.insertSpaces"&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="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.detectIndentation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;From now on, whenever someone opens your project in VS Code, pressing the &lt;code&gt;Tab&lt;/code&gt; key will insert 2 spaces, and pressing the &lt;code&gt;Backspace&lt;/code&gt; key will delete 2 spaces.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Automate Linting and Formatting
&lt;/h2&gt;

&lt;p&gt;In this section, we'll integrate checking and formatting commands into your project's NPM scripts. This enables you to execute batched tasks or run them in your &lt;strong&gt;CI workflows&lt;/strong&gt; and &lt;strong&gt;&lt;a href="https://dev.to/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d"&gt;Git Hooks&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  Linting Command
&lt;/h3&gt;

&lt;p&gt;Edit your &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;{
  "scripts": {
    "test": "echo \"Error: no test specified\" &amp;amp;&amp;amp; exit 1",
&lt;span class="gi"&gt;+   "lint": "eslint ."
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Don't forget to exclude some files that should not be linted. Create a &lt;code&gt;.eslintignore&lt;/code&gt; file and add the necessary exclusions to it.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

node_modules
test
coverage
public
dist


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now try the script:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm run lint


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;All ESLint problems in your JavaScript/TypeScript files will be displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F5gpa4zo71qjgmw5qtibq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F5gpa4zo71qjgmw5qtibq.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Formatting Command
&lt;/h3&gt;

&lt;p&gt;As mentioned above, some problems can be fixed by the command &lt;code&gt;eslint --fix&lt;/code&gt;. So We can leverage this command as a way to batch format your code.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;{
  "scripts": {
&lt;span class="gi"&gt;+   "format": "eslint --fix .",
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And try formatting it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npm run format


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Since some problems have been fixed, only those that can't be fixed will be displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fj0m0btzqe44n8qj0ic72.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fj0m0btzqe44n8qj0ic72.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Why do I need NPM scripts
&lt;/h3&gt;

&lt;p&gt;You must find that the commands of &lt;code&gt;npm run lint&lt;/code&gt; and &lt;code&gt;npm run format&lt;/code&gt; have no difference from &lt;code&gt;npx eslint .&lt;/code&gt; and &lt;code&gt;npx eslint --fix .&lt;/code&gt; respectively.&lt;/p&gt;

&lt;p&gt;Well, when you run &lt;code&gt;npx eslint .&lt;/code&gt; directly, it attempts to execute the ESLint tool from your project’s local dependencies (ESLint under the &lt;code&gt;/node_modules/.bin/&lt;/code&gt; directory of your project). However, if ESLint is not installed within your project, &lt;code&gt;npx&lt;/code&gt; will attempt to run the globally installed ESLint instead.&lt;/p&gt;

&lt;p&gt;However, the NPM script &lt;code&gt;"lint": "eslint ."&lt;/code&gt; ensures that ESLint is only executed within your project. If ESLint is not installed, running this script will result in an error.&lt;/p&gt;

&lt;p&gt;By strictly defining the specific version (or version ranges) of ESLint in your &lt;code&gt;package.json&lt;/code&gt;, you can avoid conflicts between ESLint versions installed globally and those required for your project.&lt;/p&gt;

&lt;p&gt;What's more, your &lt;code&gt;lint&lt;/code&gt; script might include additional flags or options specific to your project. NPM scripts, with more semantic indicators, allow you to abstract away those complex commands or tool configurations.&lt;/p&gt;

&lt;p&gt;I have another post on &lt;a href="https://dev.to/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d"&gt;Streamline Your Workflow: A Guide to Normalising Git Commit and Push Processes&lt;/a&gt;, leveraging the NPM scripts. Check it out for more ideas that can enhance your workflow.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/graezykev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1425699%2F36981583-6544-43e8-8660-1842a1f5941d.jpeg" alt="graezykev"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Streamline Your Workflow: A Guide to Normalising Git Commit and Push Processes&lt;/h2&gt;
      &lt;h3&gt;Kev ・ May 6&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#githook&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#husky&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#commitlint&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#lintstaged&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Linting React &amp;amp; React Hooks
&lt;/h2&gt;

&lt;p&gt;JavaScript Standard Style is less opinionated about JSX formatting and largely leaves JSX as-is. In a React project, you should integrate with React-specific linting rules for ESLint. The generally accepted configurations are &lt;a href="https://github.com/jsx-eslint/eslint-plugin-react" rel="noopener noreferrer"&gt;eslint-plugin-react&lt;/a&gt; and &lt;a href="https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks" rel="noopener noreferrer"&gt;eslint-plugin-react-hooks&lt;/a&gt;, enforcing some best practices of writing React code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;First, install React and its type definitions.&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;react react-dom


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;-D&lt;/span&gt; @types/react @types/react-dom


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Disable VS Code Extension (temporary)
&lt;/h3&gt;

&lt;p&gt;In the next steps, I'm going to create some code, but I don't want them to be auto-fixed by ESLint.&lt;/p&gt;

&lt;p&gt;So, before we proceed, let's disable the ESLint VS Code Extension to better demonstrate how the following linting configurations work. (But &lt;strong&gt;this is only an intermediate step; don't forget to undo the change in the end&lt;/strong&gt;.)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fckhucs51zef343hqpoxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fckhucs51zef343hqpoxv.png" alt="alt text"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Let TypeScript to Recognize JSX
&lt;/h3&gt;

&lt;p&gt;Next, let's create a sample file &lt;code&gt;react-code-1.tsx&lt;/code&gt; to write some React (JSX) code.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SignupButton&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSignup&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sign up successful!&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSignup&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;signup-button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;Sign&lt;/span&gt; &lt;span class="nx"&gt;Up&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&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;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyComponent&lt;/span&gt; &lt;span class="o"&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="o"&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;(&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="nx"&gt;id&lt;/span&gt;&lt;span class="o"&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="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;There are some errors visible in the editor.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fi769sabr01v588jm4c1c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fi769sabr01v588jm4c1c.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's because JSX syntax is not allowed yet in TypeScript configuration.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;tsconfig.json&lt;/code&gt; to enable it:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gd"&gt;-    // "jsx": "preserve",                                /* Specify what JSX code is generated. */
&lt;/span&gt;&lt;span class="gi"&gt;+    "jsx": "react",                                /* Specify what JSX code is generated. */
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Then the error above will disappear.&lt;/p&gt;

&lt;p&gt;Next, lint the &lt;code&gt;.tsx&lt;/code&gt; file:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint react-code-1.tsx


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You'll find 14 problems, indicating that ESLint is enabled for &lt;code&gt;.tsx&lt;/code&gt; files.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Frktjwvr9ccxpzu6ur2tf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Frktjwvr9ccxpzu6ur2tf.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Ask ESLint to Recognize &lt;code&gt;.jsx&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;react-code-2.jsx&lt;/code&gt; file with the same code as &lt;code&gt;react-code-1.tsx&lt;/code&gt;, and lint it.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint react-code-2.jsx


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You may see some problems, but they are not actual code style issues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fuisoautb791s8srjm6a9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fuisoautb791s8srjm6a9.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's because the &lt;code&gt;.jsx&lt;/code&gt; file extension is not specified in the ESLint configuration. Let's configure it in &lt;code&gt;eslint.config.js&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;export default [
&lt;/span&gt;&lt;span class="gd"&gt;-  { files: ['**/*.js'], languageOptions: { sourceType: 'script' } },
&lt;/span&gt;&lt;span class="gi"&gt;+  { files: ['**/*.{js,ts,jsx,tsx}'], languageOptions: { sourceType: 'script' } },
&lt;/span&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Lint it again, and you'll get &lt;strong&gt;14 errors&lt;/strong&gt;, the same as &lt;code&gt;react-code-1.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F33qgvg49zyrmm547nxno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F33qgvg49zyrmm547nxno.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These errors in &lt;code&gt;react-code-1.tsx&lt;/code&gt; and &lt;code&gt;react-code-2.jsx&lt;/code&gt; are code style problems, however, they are still not React-specific problems. We need to do more.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install React &amp;amp; React Hooks ESLint plugins
&lt;/h3&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;-D&lt;/span&gt; eslint-plugin-react eslint-plugin-react-hooks


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Configure React ESLint Plugin
&lt;/h3&gt;

&lt;p&gt;First, configure &lt;code&gt;eslint-plugin-react&lt;/code&gt; in &lt;code&gt;eslint.config.js&lt;/code&gt; with just a new line.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;export default [
&lt;/span&gt;  { files: ['**/*.{js,ts,jsx,tsx}'], languageOptions: { sourceType: 'script' } },
  { languageOptions: { globals: globals.browser } },
&lt;span class="gi"&gt;+ ...compat.extends('plugin:react/recommended'),
&lt;/span&gt;  ...tseslint.configs.recommended,
  ...compat.extends('love')
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Run command to lint it.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint react-code-1.tsx


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;You'll find &lt;strong&gt;15 problems&lt;/strong&gt;. This is one more issue compared to the previous &lt;strong&gt;14 problems&lt;/strong&gt;, because an additional problem related to React, specified in the plugin &lt;code&gt;eslint-plugin-react&lt;/code&gt;, has been detected.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Ff0kjb2hbi1k5ui4jdd5r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Ff0kjb2hbi1k5ui4jdd5r.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Self-Customize your Own Rule(s)
&lt;/h3&gt;

&lt;p&gt;Ok, let's take one further step and edit &lt;code&gt;eslint.config.js&lt;/code&gt; to extend your custom rule:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="p"&gt;export default [
&lt;/span&gt;  { files: ['**/*.{js,ts,jsx,tsx}'], languageOptions: { sourceType: 'script' } },
  { languageOptions: { globals: globals.browser } },
  ...compat.extends('plugin:react/recommended'),
  ...tseslint.configs.recommended,
  ...compat.extends('love'),
&lt;span class="gi"&gt;+  {
+    rules: {
+      'react/destructuring-assignment': ['warn', 'always']
+    }
+  }
&lt;/span&gt;]
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Run &lt;code&gt;npx eslint react-code-1.tsx&lt;/code&gt;, and you'll notice one additional problem defined by you.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fbbcmx08vypkybas5d1x3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fbbcmx08vypkybas5d1x3.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can define or modify any rules as much as you like to tailor your team's rules here.&lt;/p&gt;
&lt;h3&gt;
  
  
  Configure React Hooks ESLint Plugin
&lt;/h3&gt;

&lt;p&gt;Let's now integrate the best practice rules for React Hooks.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;react-code-1.tsx&lt;/code&gt; to include some React Hooks code.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;&lt;span class="gd"&gt;-import React from "react";
&lt;/span&gt;&lt;span class="gi"&gt;+import React, { useContext } from "react";
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;...
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="p"&gt;const MyComponent = (props) =&amp;gt; {
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gi"&gt;+  if (true) {
+    const theme = useContext(ThemeContext);
+  }
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;  return (&amp;lt;div id={props.id} /&amp;gt;)
};
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now lint it.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

npx eslint react-code-1.tsx


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;And you'll find some errors, but &lt;strong&gt;none of them are related to React Hooks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fot4zjmng7w6ow1jape34.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fot4zjmng7w6ow1jape34.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You just need to edit the ESLint configuration with one additional line.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  ...compat.extends('plugin:react/recommended'),
&lt;span class="gi"&gt;+ ...compat.extends('plugin:react-hooks/recommended'),
&lt;/span&gt;  ...tseslint.configs.recommended,
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Lint &lt;code&gt;react-code-1.tsx&lt;/code&gt; again, and you'll get errors related to React Hooks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2F763mfkttb3ydd6kdy68i.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2F763mfkttb3ydd6kdy68i.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's one more thing you may have noticed: the warning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.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%2Fcnhrc31ox0lad654ccvi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.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%2Fcnhrc31ox0lad654ccvi.png" alt="ts eslint standard js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Add the following configurations to automatically detect the React version and eliminate the warning.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="err"&gt;

&lt;/span&gt;  ...compat.extends('love'),
  {
&lt;span class="gi"&gt;+    settings: {
+      react: {
+        version: 'detect'
+      }
+    },
&lt;/span&gt;    rules: {
&lt;span class="err"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now we have a somewhat comprehensive toolchain for linting and formatting your code.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;TypeScript and JavaScript&lt;/strong&gt; code, including features developed with &lt;strong&gt;React and React Hooks&lt;/strong&gt;, will be &lt;strong&gt;validated and formatted&lt;/strong&gt; using &lt;strong&gt;CLI scripts and Editor Tools&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We also have a method to &lt;strong&gt;customise&lt;/strong&gt; the rules for React and React Hooks or to extend them with your own rules.&lt;/p&gt;

&lt;p&gt;Next, you may want to integrate your linting scripts with your Git Hooks or CI/CD workflows. For Git Hooks, I have another post that elaborates on the steps. Check it out if you're interested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d"&gt;Streamline Your Workflow: A Guide to Normalising Git Commit and Push Processes&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/graezykev" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1425699%2F36981583-6544-43e8-8660-1842a1f5941d.jpeg" alt="graezykev"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/graezykev/streamline-your-workflow-a-guide-to-normalising-git-commit-and-push-processes-1o7d" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Streamline Your Workflow: A Guide to Normalising Git Commit and Push Processes&lt;/h2&gt;
      &lt;h3&gt;Kev ・ May 6&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#githook&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#husky&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#commitlint&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#lintstaged&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Last but not least, any suggestions, corrections, questions, or disagreements will be greatly appreciated.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>codequality</category>
      <category>standardjs</category>
      <category>eslint</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
