<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Reza Owliaei</title>
    <description>The latest articles on Forem by Reza Owliaei (@rezaowliaei).</description>
    <link>https://forem.com/rezaowliaei</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%2F166885%2Fcc74eb78-ea96-4ecb-8c5a-1ed43c9f2804.jpg</url>
      <title>Forem: Reza Owliaei</title>
      <link>https://forem.com/rezaowliaei</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rezaowliaei"/>
    <language>en</language>
    <item>
      <title>Simplifying Code Quality with a Unified Biome Configuration</title>
      <dc:creator>Reza Owliaei</dc:creator>
      <pubDate>Tue, 05 Aug 2025 11:37:56 +0000</pubDate>
      <link>https://forem.com/rezaowliaei/simplifying-code-quality-with-a-unified-biome-configuration-jah</link>
      <guid>https://forem.com/rezaowliaei/simplifying-code-quality-with-a-unified-biome-configuration-jah</guid>
      <description>&lt;p&gt;&lt;strong&gt;Tired of juggling ESLint, Prettier, and import sorters just to keep your code clean?&lt;/strong&gt; You’re not alone.&lt;/p&gt;

&lt;p&gt;Modern development shouldn’t be slowed down by a fragmented toolchain.&lt;/p&gt;

&lt;p&gt;This post introduces &lt;a href="https://biomejs.dev" rel="noopener noreferrer"&gt;Biome&lt;/a&gt;: a fast, modern alternative to the legacy combo of ESLint + Prettier + plugins. With just &lt;strong&gt;one tool, one config, and one command&lt;/strong&gt;, Biome handles formatting, linting, and organizing imports—without the overhead.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This guide is for developers looking to simplify tooling, increase speed, and write consistent code with fewer tools.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  What Is Biome?
&lt;/h2&gt;

&lt;p&gt;Biome is a &lt;strong&gt;single, unified CLI tool&lt;/strong&gt; (written in Rust) that replaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESLint + plugins&lt;/li&gt;
&lt;li&gt;Prettier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lint-staged&lt;/code&gt; and &lt;code&gt;husky&lt;/code&gt; scripts&lt;/li&gt;
&lt;li&gt;Sorting tools like &lt;code&gt;eslint-plugin-import&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Formatting&lt;/li&gt;
&lt;li&gt;Linting (both bug-catching and style)&lt;/li&gt;
&lt;li&gt;Import sorting&lt;/li&gt;
&lt;li&gt;Git-aware checks (&lt;code&gt;--staged&lt;/code&gt;, &lt;code&gt;--changed&lt;/code&gt;, &lt;code&gt;--since&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Fast performance out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You configure it all in one place: &lt;code&gt;biome.json&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Installing Biome
&lt;/h2&gt;

&lt;p&gt;Install Biome using your preferred package manager:&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Bun
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun add &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; @biomejs/biome
bunx @biomejs/biome init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using npm
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; @biomejs/biome
npx @biomejs/biome init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a base &lt;code&gt;biome.json&lt;/code&gt; config in your project.&lt;/p&gt;




&lt;h2&gt;
  
  
  Biome in Action
&lt;/h2&gt;

&lt;p&gt;Here are the main commands you’ll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;biome check&lt;/code&gt; – runs formatting, linting, and import sorting together. Use &lt;code&gt;--write&lt;/code&gt; to apply fixes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;biome format&lt;/code&gt; – formats only.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;biome lint&lt;/code&gt; – lints only.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;biome ci&lt;/code&gt; – read-only, used in CI pipelines.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;--staged&lt;/code&gt;, &lt;code&gt;--changed&lt;/code&gt;, or &lt;code&gt;--since=origin/main&lt;/code&gt; with any command to scope it to Git changes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Why I Chose Biome (Configuration Philosophy)
&lt;/h2&gt;

&lt;p&gt;Biome solves real-world pain points from the old ecosystem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One tool, zero glue code.&lt;/strong&gt; No more connecting Prettier to ESLint with plugins and linters to formatters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Catch real bugs, not nitpicks.&lt;/strong&gt; Biome includes useful rules like:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;noUndeclaredVariables&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;noDoubleEquals&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;noAccumulatingSpread&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexible rule levels.&lt;/strong&gt; You can mark style rules as warnings and true problems as errors.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context-aware overrides.&lt;/strong&gt; Need to allow &lt;code&gt;console.log&lt;/code&gt; in tests or &lt;code&gt;default export&lt;/code&gt; in Next.js pages? Overrides make that easy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Modern tooling mindset.&lt;/strong&gt; Supports monorepos, CI-first workflows, Git-aware scanning, and blazing speed from Rust.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  My Biome Config (from real projects)
&lt;/h2&gt;

&lt;p&gt;You can find the complete, production-ready config I use here:&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://gist.github.com/RezaOwliaei/948eed09f4232f3330747ea5e4cf607d" rel="noopener noreferrer"&gt;View the biome.json on GitHub Gist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Highlights include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tabs instead of spaces (accessibility-friendly)&lt;/li&gt;
&lt;li&gt;100 character line width&lt;/li&gt;
&lt;li&gt;Git-aware scanning via &lt;code&gt;vcs&lt;/code&gt; config&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Overrides for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;test files (console.log allowed)&lt;/li&gt;
&lt;li&gt;Next.js layouts/pages (default export allowed)&lt;/li&gt;
&lt;li&gt;config files (&lt;code&gt;vite.config.ts&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;Global test functions like &lt;code&gt;describe&lt;/code&gt;, &lt;code&gt;it&lt;/code&gt;, &lt;code&gt;expect&lt;/code&gt;, etc.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Feel free to copy and adapt it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Suggested &lt;code&gt;package.json&lt;/code&gt; Scripts
&lt;/h2&gt;

&lt;p&gt;For smooth local dev experience:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"check"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome check ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"check:fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome check --write ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome lint ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint:fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome lint --write ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fmt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome format ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fmt:fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome format --write ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"check:staged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"biome check --staged --write"&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;These commands let you quickly check everything or apply fixes, locally or in CI.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pre-commit Hook with Husky
&lt;/h2&gt;

&lt;p&gt;To prevent broken code from being committed, run Biome only on staged files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx husky-init &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install
echo&lt;/span&gt; &lt;span class="s1"&gt;'biome check --staged --write'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .husky/pre-commit
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x .husky/pre-commit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures formatting, import order, and critical lint rules are enforced before every commit.&lt;/p&gt;




&lt;h2&gt;
  
  
  CI Integration (GitHub Actions)
&lt;/h2&gt;

&lt;p&gt;Use this to run Biome on pull requests and pushes:&lt;br&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;code-quality&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;biome&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;biomejs/setup-biome@v2&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;biome ci --changed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This checks only files changed in the PR, keeping CI fast and focused.&lt;/p&gt;




&lt;h2&gt;
  
  
  Editor Integration (VS Code)
&lt;/h2&gt;

&lt;p&gt;Install the &lt;a href="https://marketplace.visualstudio.com/items?itemName=biomejs.biome" rel="noopener noreferrer"&gt;Biome VS Code Extension&lt;/a&gt;, then add the following to &lt;code&gt;.vscode/settings.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"biomejs.biome"&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.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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.codeActionsOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source.organizeImports.biome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"explicit"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"source.fixAll.biome"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"explicit"&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 enables automatic formatting, import sorting, and rule fixes on save.&lt;/p&gt;




&lt;h2&gt;
  
  
  Monorepo Support (Nx / Turborepo)
&lt;/h2&gt;

&lt;p&gt;Biome supports nested configuration out of the box.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  At the root (&lt;code&gt;biome.json&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"linter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"recommended"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"formatter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"lineWidth"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Inside a package (e.g., &lt;code&gt;packages/web/biome.json&lt;/code&gt;)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"//"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"root"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows per-package customizations while inheriting defaults from the root.&lt;/p&gt;




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

&lt;p&gt;Biome is the next step in code quality:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No more endless plugins&lt;/li&gt;
&lt;li&gt;No more syncing configs across tools&lt;/li&gt;
&lt;li&gt;Just one file and one command to format, lint, and sort&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers starting fresh, Biome offers a clean, modern, and efficient approach to code quality—and makes legacy stacks feel like a thing of the past.&lt;/p&gt;




&lt;p&gt;Want help adopting Biome in your project? Have questions or examples to share? Let me know in the comments or reach out directly.&lt;/p&gt;

</description>
      <category>linter</category>
      <category>biome</category>
      <category>eslint</category>
      <category>prettier</category>
    </item>
    <item>
      <title>Seccomp in Docker: Advanced System Call Filtering for a Hardened Container Runtime</title>
      <dc:creator>Reza Owliaei</dc:creator>
      <pubDate>Sat, 12 Jul 2025 09:39:57 +0000</pubDate>
      <link>https://forem.com/rezaowliaei/seccomp-in-docker-advanced-system-call-filtering-for-a-hardened-container-runtime-4a5h</link>
      <guid>https://forem.com/rezaowliaei/seccomp-in-docker-advanced-system-call-filtering-for-a-hardened-container-runtime-4a5h</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As modern applications increasingly run in containers, the security of the container runtime becomes paramount. One of the most powerful—but often overlooked—mechanisms for hardening Linux-based containers is &lt;strong&gt;seccomp&lt;/strong&gt; (Secure Computing Mode). Seccomp enables fine-grained filtering of system calls, allowing you to limit what your applications can ask of the kernel.&lt;/p&gt;

&lt;p&gt;In this post, we’ll go beyond surface-level explanations and dive into how seccomp works, why it matters in real-world production environments, and how to leverage it effectively using Docker. You’ll learn how to inspect, customize, and enforce syscall-level security, ensuring containers operate with the least privilege necessary.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;What Is Seccomp?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Seccomp is a Linux kernel feature that allows processes to restrict the system calls (syscalls) they are permitted to make. By default, a Linux process can invoke hundreds of syscalls. However, most applications use only a small subset.&lt;/p&gt;

&lt;p&gt;By blocking unnecessary syscalls, seccomp:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Reduces the kernel attack surface&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Prevents certain classes of container breakout exploits&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Enforces least privilege at the syscall level&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is especially important in containers, where applications run in shared-kernel environments. Even with other isolation mechanisms like namespaces and cgroups in place, unfiltered access to the syscall interface can lead to privilege escalation or host compromise.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Seccomp Operating Modes&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Seccomp supports two modes:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Strict Mode&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The original implementation (enabled via prctl) restricts a process to only four syscalls:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;read(2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;write(2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;_exit(2)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;sigreturn(2)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This mode is rarely used outside of specific academic or embedded contexts due to its extreme limitations.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Filter Mode (seccomp-bpf)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The modern and practical variant uses &lt;strong&gt;Berkeley Packet Filter (BPF)&lt;/strong&gt; programs to evaluate syscalls at runtime. You can allow, block, log, or return errors based on syscall type, arguments, and context. This is the mode used by Docker and Kubernetes.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Why Seccomp Matters for Container Security&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;In containerized environments, the kernel is shared among containers and the host. A compromised container that can execute privileged syscalls becomes a potential entry point to the entire system. Seccomp provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Granular Syscall Control&lt;/strong&gt;: Limit actions like creating raw sockets, mounting filesystems, or spawning new namespaces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Exploit Mitigation&lt;/strong&gt;: Many Linux kernel vulnerabilities rely on specific syscalls. Blocking them disrupts exploit chains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Defense in Depth&lt;/strong&gt;: Adds a syscall-level layer to existing security features like AppArmor, SELinux, and user namespaces.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compliance and Auditability&lt;/strong&gt;: You can demonstrate precise control over application behavior down to the syscall level.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;How Docker Uses Seccomp&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Docker applies a &lt;strong&gt;default seccomp profile&lt;/strong&gt; to all containers unless overridden. This profile is maintained by the Moby project and is reasonably conservative: it blocks approximately 44 syscalls out of over 300 available on a typical x86_64 system.&lt;/p&gt;

&lt;p&gt;Blocked syscalls include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;keyctl (used for kernel key management)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;add_key, request_key&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ptrace&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mount (unless explicitly enabled)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The default profile allows safe defaults for most containerized workloads while blocking dangerous or rarely-used syscalls.&lt;/p&gt;

&lt;p&gt;You can view the official profile &lt;a href="https://github.com/moby/moby/blob/master/profiles/seccomp/default.json" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Anatomy of a&lt;/strong&gt;  &lt;code&gt;seccomp.json&lt;/code&gt; Profile
&lt;/h2&gt;

&lt;p&gt;A seccomp profile is a JSON document that defines syscall rules for a process. Key fields include:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;defaultAction&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;What to do with syscalls that aren’t explicitly listed. Common options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;SCMP_ACT_ERRNO: Return a permission error&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SCMP_ACT_KILL: Immediately terminate the process&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SCMP_ACT_ALLOW: Allow the syscall&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;SCMP_ACT_TRACE, SCMP_ACT_LOG: Advanced debugging options&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;architectures&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Specifies applicable architectures. Typical values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;["SCMP_ARCH_X86", "SCMP_ARCH_X86_64", "SCMP_ARCH_X32"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;syscalls&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;An array defining syscall rules, where each item includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;name (or names): syscall name(s)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;action: what to do if called&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;args (optional): match syscall arguments for conditional rules&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Minimal example:&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "name": "mkdir",
      "action": "SCMP_ACT_ERRNO",
      "args": []
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will deny the mkdir syscall for all processes using the profile, regardless of arguments.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Using Custom Seccomp Profiles in Docker&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can supply your own seccomp profile when running containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run \
  --security-opt seccomp=/path/to/custom-seccomp.json \
  your-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To disable seccomp entirely (not recommended unless debugging):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run --security-opt seccomp=unconfined your-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Example: Blocking Network-Related Syscalls&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here’s a custom profile that blocks network access by denying socket, connect, and mkdir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    { "name": "socket", "action": "SCMP_ACT_ERRNO" },
    { "name": "connect", "action": "SCMP_ACT_ERRNO" },
    { "name": "mkdir", "action": "SCMP_ACT_ERRNO" }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Applying this profile to a container means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;ping, curl, or anything that uses sockets will fail&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;mkdir operations will also fail, even as root&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;All other syscalls will return permission errors unless explicitly allowed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Best Practices for Seccomp Usage&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with the default profile&lt;/strong&gt; and incrementally restrict based on runtime analysis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use syscall tracing tools&lt;/strong&gt; like strace, sysdig, or bpftrace to identify which syscalls your app needs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don’t over-optimize early&lt;/strong&gt;. Avoid blocking syscalls your app might need during boot or init.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Version and audit your profiles&lt;/strong&gt; alongside your infrastructure code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Combine seccomp with other controls&lt;/strong&gt; like AppArmor, SELinux, cgroups, and capabilities for layered defense.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Advanced: Conditional Filtering with&lt;/strong&gt; 
&lt;/h2&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;args&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can apply fine-grained rules using the args field. For example, allow the socket syscall only for TCP connections (AF_INET, SOCK_STREAM):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "name": "socket",
  "action": "SCMP_ACT_ERRNO",
  "args": [
    {
      "index": 0,
      "value": 2,
      "op": "SCMP_CMP_EQ"
    },
    {
      "index": 1,
      "value": 1,
      "op": "SCMP_CMP_EQ"
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This filters the syscall only when both arguments match the expected values for an IPv4 TCP socket.&lt;/p&gt;




&lt;p&gt;Seccomp provides syscall-level control over application behavior, reducing attack surface and enforcing least privilege. In a containerized world where isolation boundaries are thinner than traditional virtual machines, mechanisms like seccomp are essential for building secure, production-grade systems.&lt;/p&gt;

&lt;p&gt;Whether you’re securing microservices, isolating CI pipelines, or just building better defaults, understanding and applying seccomp is an important step in your journey toward mature container security.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Further Reading and Tools&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/moby/moby/blob/master/profiles/seccomp/default.json" rel="noopener noreferrer"&gt;Default Docker Seccomp Profile (Moby Project)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://man7.org/linux/man-pages/man1/strace.1.html" rel="noopener noreferrer"&gt;strace syscall tracer&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://sysdig.com/opensource/sysdig/" rel="noopener noreferrer"&gt;Sysdig Open Source&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/opencontainers/runtime-spec/blob/main/config-linux.md#seccomp" rel="noopener noreferrer"&gt;OCI Runtime Spec: Seccomp&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>docker</category>
      <category>container</category>
      <category>linux</category>
    </item>
    <item>
      <title>The Battle of Node.js Testing: When Switching Back to Mocha Seemed Like easier</title>
      <dc:creator>Reza Owliaei</dc:creator>
      <pubDate>Tue, 03 Sep 2024 16:25:42 +0000</pubDate>
      <link>https://forem.com/rezaowliaei/the-battle-of-nodejs-testing-when-switching-back-to-mocha-seemed-like-easier-4ag0</link>
      <guid>https://forem.com/rezaowliaei/the-battle-of-nodejs-testing-when-switching-back-to-mocha-seemed-like-easier-4ag0</guid>
      <description>&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;Testing is essential in software development, but it often feels like a battleground. I recently faced a challenging issue while working with the native Node.js test runner, specifically when testing asynchronous processes related to PostgreSQL and EventStoreDB. My frustration peaked several times, nearly pushing me to switch back to the ever-reliable Mocha. I thought my choice of using the native solution was the problem. As it turned out, the real issue wasn't the test framework itself but rather how I managed open resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Setup
&lt;/h3&gt;

&lt;p&gt;I had been working on a microservices project where I used Event Store to implement event sourcing, which was a core part of the system's architecture. Naturally, my test suite needed to interact with the Event Store to verify that events were correctly appended, read, and handled.&lt;/p&gt;

&lt;p&gt;Choosing Node.js’s native test runner (&lt;code&gt;node:test&lt;/code&gt;) seemed modern and efficient, mainly because it was lightweight and didn’t require extra dependencies. I set up my tests using the &lt;code&gt;EventStoreDBClient&lt;/code&gt; from the &lt;code&gt;@eventstore/db-client&lt;/code&gt; library. Here’s a simplified version of what my test setup looked like:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Event Store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should connect to the eventStore and perform operations&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendToStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;TEST_STREAM&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;testEventJson&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&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="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Issue
&lt;/h3&gt;

&lt;p&gt;Initially, everything seemed fine when I ran the tests. However, I soon noticed that the test runner didn't exit after the tests completed. The test suite would pass, but the process kept running, causing intermittent hangs.&lt;/p&gt;

&lt;p&gt;I tried various approaches: using &lt;code&gt;.then()&lt;/code&gt; and &lt;code&gt;.finally()&lt;/code&gt;, implementing &lt;code&gt;try/catch&lt;/code&gt; blocks, working with &lt;code&gt;done()&lt;/code&gt;, and even using &lt;code&gt;await&lt;/code&gt; directly in the &lt;code&gt;it&lt;/code&gt; block. Despite all these attempts, the problem persisted. The frustration tempted me to revert to Mocha—a framework that had never let me down.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Discovery
&lt;/h3&gt;

&lt;p&gt;At this point, I took a step back and considered what could be causing the test runner process to hang. My past experience with databases and file handles reminded me that open connections often cause Node.js processes to hang, waiting for those connections to close.&lt;/p&gt;

&lt;p&gt;This realization prompted me to review my resource management. I discovered that the &lt;code&gt;EventStoreDBClient&lt;/code&gt; instance needed to be properly disposed of after each test run. While I had an &lt;code&gt;after&lt;/code&gt; hook to dispose of the client, I hadn’t ensured that all resources were being closed rigorously.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Fix
&lt;/h3&gt;

&lt;p&gt;To address the issue, I made sure to explicitly call the &lt;code&gt;dispose()&lt;/code&gt; method on the Event Store client after the tests had finished. This ensured that any open connections were closed, and resources were cleaned up properly:&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="nf"&gt;after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispose&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Ensure all resources are properly closed&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lessons Learned
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resource Management is Crucial&lt;/strong&gt;: Always check for open resources, such as database connections, file handles, or network connections. Failing to properly dispose of these can cause tests to hang or fail intermittently.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Don’t Blame the Framework Too Quickly&lt;/strong&gt;: It’s tempting to blame the testing tool when things go wrong. However, the issue often lies within the implementation. Understanding the tools and properly managing resources can solve many problems.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Using the native Node.js test runner was a valuable learning experience. Although the temptation to revert to Mocha was strong, resolving the resource management issue highlighted the importance of proper setup and teardown processes in automated testing. Ultimately, it’s not about the tool you choose but how effectively you use it. Properly managing resources, understanding the framework, and thorough testing practices will lead to a more stable and reliable testing environment.&lt;/p&gt;

</description>
      <category>node</category>
      <category>eventstoredb</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
