<?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>Scale Forem</title>
    <description>The most recent home feed on Scale Forem.</description>
    <link>https://scale.forem.com</link>
    <atom:link rel="self" type="application/rss+xml" href="https://scale.forem.com/feed"/>
    <language>en</language>
    <item>
      <title>BullMQ + Node.js: Replace 50 Cron Jobs with Smart Queues</title>
      <dc:creator>Кирилл</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:12:23 +0000</pubDate>
      <link>https://scale.forem.com/_1353e04f14b156240b/bullmq-nodejs-replace-50-cron-jobs-with-smart-queues-3j3n</link>
      <guid>https://scale.forem.com/_1353e04f14b156240b/bullmq-nodejs-replace-50-cron-jobs-with-smart-queues-3j3n</guid>
      <description>&lt;h1&gt;
  
  
  BullMQ + Node.js: замена 50 cron-задач на умные очереди
&lt;/h1&gt;

&lt;h1&gt;
  
  
  BullMQ + Node.js: Замена 50 Cron-задач на Умные Очереди
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Введение
&lt;/h2&gt;

&lt;p&gt;В этой статье мы рассмотрим, как заменить 50 cron-��адач на умные очереди с помощью BullMQ и Node.js. Мы погрузимся в преимущества использования очередей сообщений, настройку BullMQ и предоставим практические примеры того, как интегрировать его с Node.js.&lt;/p&gt;

&lt;h2&gt;
  
  
  Проблема с Cron-задачами
&lt;/h2&gt;

&lt;p&gt;Cron-задачи являются распространенным способом планирования задач в системах Linux. Однако, по мере роста количества задач, управление cron-задачами может стать громоздким. У нас было 50 cron-задач, запущенных в нашей системе, каждая со своей собственной планировкой и логикой. Это привело к:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Трудностям в управлении и масштабировании системы&lt;/li&gt;
&lt;li&gt;Увеличению риска ошибок и конфликтов между задачами&lt;/li&gt;
&lt;li&gt;Ограниченной видимости выполнения задач и их производительности&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Введение в BullMQ
&lt;/h2&gt;

&lt;p&gt;BullMQ — это библиотека Node.js, которая предоставляет про��той и эффективный способ управления очередями сообщений. Она позволяет создавать очереди, добавлять задачи в них и обрабатывать эти задачи в надежном и масштабируемом виде. С помощью BullMQ мы можем заменить наши cron-задачи на умные очереди, которые предоставляют:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Лучшую масштабируемость и производительность&lt;/li&gt;
&lt;li&gt;Улучшенную видимость выполнения задач и их производительности&lt;/li&gt;
&lt;li&gt;Упрощенное управление и обработку ошибок&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Настройка BullMQ
&lt;/h2&gt;

&lt;p&gt;Чтобы начать работу с BullMQ, необходимо установить пакет &lt;code&gt;bull&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;npm &lt;span class="nb"&gt;install &lt;/span&gt;bull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Далее, создайте новый файл для своей очереди, например, &lt;code&gt;queue.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Queue&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;bull&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;queue&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;Queue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myQueue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6379&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом примере мы создаем новую очередь с именем &lt;code&gt;myQueue&lt;/code&gt;, которая использует Redis в качестве движка хранения.&lt;/p&gt;

&lt;h2&gt;
  
  
  Добавление Задач в Очередь
&lt;/h2&gt;

&lt;p&gt;Чтобы добавить задачу в очередь, можно использовать метод &lt;code&gt;add&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;queue&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;./queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;myJob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом примере мы добавляем новую задачу в очередь с именем &lt;code&gt;myJob&lt;/code&gt; и некоторыми образцами данных.&lt;/p&gt;

&lt;h2&gt;
  
  
  Обработка Задач
&lt;/h2&gt;

&lt;p&gt;Чтобы обработать задачи, необходимо создать рабочий процесс, который потребляет задачи из очереди:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;queue&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;./queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&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;`Обработка задачи &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;job&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="s2"&gt; с данными: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&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="c1"&gt;// Обработка задачи здесь&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом примере мы создаем рабочий процесс, который потребляет задачи из очереди и выводит некоторую информацию о задаче.&lt;/p&gt;

&lt;h2&gt;
  
  
  Замена Cron-задач на Умные Очереди
&lt;/h2&gt;

&lt;p&gt;Чтобы заменить наши 50 cron-задач на умные очереди, мы можем создать одну очередь, которая обрабатывает все задачи. Мы можем затем добавлять задачи в очередь с разными планировками и приоритетами.&lt;/p&gt;

&lt;p&gt;Например, мы можем создать задачу, которая запускается каждый час:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;queue&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;./queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hourlyJob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 * * * *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом примере мы добавляем новую задачу в очередь, которая запускается каждый час.&lt;/p&gt;

&lt;p&gt;Мы также можем создать задачу, которая запускается каждый день в 2 часа ночи:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;queue&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;./queue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dailyJob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;repeat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0 2 * * *&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;В этом примере мы добавляем новую задачу в очередь, которая запускается каждый день в 2 часа ночи.&lt;/p&gt;

&lt;h2&gt;
  
  
  Преимущества Использования BullMQ
&lt;/h2&gt;

&lt;p&gt;Использование BullMQ предоставляет несколько преимуществ, включая:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Улучшенную масштабируемость и производительность&lt;/li&gt;
&lt;li&gt;Упрощенное управление и обработку ошибок&lt;/li&gt;
&lt;li&gt;Лучшую видимость выполнения задач и их производительности&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Заключение
&lt;/h2&gt;

&lt;p&gt;В этой статье мы рассмотрели, как заменить 50 cron-задач на умные очереди с помощью BullMQ и Node.js. Мы погрузились в преимущества использования очередей сообщений, настройку BullMQ и предоставили практические примеры того, как интегрировать его с Node.js.&lt;/p&gt;




&lt;p&gt;Если интересно — подписывайтесь, будет ещё.&lt;/p&gt;

</description>
      <category>node</category>
      <category>redis</category>
      <category>tutorial</category>
      <category>backend</category>
    </item>
    <item>
      <title>I Built Boreal UI — An Accessibility-First Component Library for React and Next.js</title>
      <dc:creator>Davin Chiupka</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:10:37 +0000</pubDate>
      <link>https://scale.forem.com/davec6662/i-built-boreal-ui-an-accessibility-first-component-library-for-react-and-nextjs-1pi3</link>
      <guid>https://scale.forem.com/davec6662/i-built-boreal-ui-an-accessibility-first-component-library-for-react-and-nextjs-1pi3</guid>
      <description>&lt;p&gt;During my two semesters of project management classes in college, we had roughly four months to come up with an idea, build an MVP, and complete the full project for our final grade. It was a lot to juggle and sometimes a little chaotic, but it taught me a lot about how real product development works. We had to think about user stories, planning, databases, back-end logic, wireframes, and front-end implementation all at the same time.&lt;/p&gt;

&lt;p&gt;Even with all of that, I was always most drawn to the front-end side. I’ve always enjoyed the creative side of development, and one of my favorite parts of the process was seeing wireframes turn into a real, usable interface. The problem was that every time we built a new application, I wanted to create the UI from scratch.&lt;/p&gt;

&lt;p&gt;A lot of the existing options just did not click with me. Many UI libraries felt too familiar, a little too corporate for my taste, or harder to shape into exactly what I wanted. I often felt like I was either stuck with someone else’s visual style or spending too much time fighting the framework to make it feel like my own.&lt;/p&gt;

&lt;p&gt;So, after those classes ended, I made the very ambitious, and maybe slightly irrational, decision to build my own UI library from the ground up. Since I already loved building with React, I decided to make it work for both React Core and Next.js.&lt;/p&gt;

&lt;p&gt;That became &lt;strong&gt;Boreal UI&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Boreal UI has been the result of a lot of learning, trial and error, and time spent building and rebuilding components. The core idea behind it was simple: UI libraries should help developers create, not force them into a default look and feel. Components should be flexible enough to support creativity rather than limit it.&lt;/p&gt;

&lt;p&gt;That is why Boreal UI puts such a strong focus on customization. One of my favorite features is the ability to define project-wide defaults, so instead of styling every component over and over, the whole app can feel consistent from the start.&lt;/p&gt;

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

&lt;p&gt;It also includes built-in color schemes, including both light and dark themes, and you can register your own custom schemes as well. That means if you want your app to reflect your own branding or style, you can do that without a ton of extra setup.&lt;/p&gt;

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

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

&lt;p&gt;Accessibility was just as important to me. During my co-op at Fanshawe’s OER Studio, I worked on open educational resources like digital textbooks and interactive learning activities, and accessibility was always a major priority. The goal was to make sure those materials could be used by anyone without barriers, and I wanted that same thinking to carry into this project. Boreal UI was built to support websites that are easier for everyone to use.&lt;/p&gt;

&lt;p&gt;So really, Boreal UI is my attempt to make building websites faster, more accessible, and more expressive at the same time. I wanted something practical, but also something that still leaves room for creativity.&lt;/p&gt;

&lt;p&gt;If you want to check out Boreal UI, you can find the full documentation at &lt;a href="https://www.borealui.ca/" rel="noopener noreferrer"&gt;borealui.ca&lt;/a&gt; or install it with npm install boreal-ui.&lt;/p&gt;

&lt;p&gt;I’d love to hear what you think. Try it out, build something with it, push it around a bit, and if you manage to break it, definitely let me know.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thanks for reading.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>nextjs</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>The Dependency Firewall: Isolate AI Changes So One Bad Prompt Can't Break Your Build</title>
      <dc:creator>Nova Elvaris</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:08:28 +0000</pubDate>
      <link>https://scale.forem.com/novaelvaris/the-dependency-firewall-isolate-ai-changes-so-one-bad-prompt-cant-break-your-build-3f6g</link>
      <guid>https://scale.forem.com/novaelvaris/the-dependency-firewall-isolate-ai-changes-so-one-bad-prompt-cant-break-your-build-3f6g</guid>
      <description>&lt;p&gt;One bad AI-generated change shouldn't cascade through your entire codebase. But without guardrails, that's exactly what happens.&lt;/p&gt;

&lt;p&gt;I call this the &lt;strong&gt;Dependency Firewall&lt;/strong&gt; — a pattern borrowed from SRE blast-radius thinking, applied to AI-assisted coding.&lt;/p&gt;

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

&lt;p&gt;You ask your AI assistant to refactor a utility function. It "helpfully" updates the function signature, changes the return type, and touches three callers. Your tests pass locally — but a downstream service that imports that module breaks in production.&lt;/p&gt;

&lt;p&gt;The root cause: &lt;strong&gt;no blast-radius boundary&lt;/strong&gt; between AI-generated changes and the rest of your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern
&lt;/h2&gt;

&lt;p&gt;Before any AI-assisted code change, define a &lt;strong&gt;change boundary&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gu"&gt;## Change Boundary&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Files allowed to change: src/utils/parser.ts
&lt;span class="p"&gt;-&lt;/span&gt; Files NOT allowed to change: anything importing parser.ts
&lt;span class="p"&gt;-&lt;/span&gt; Interface contract: parseInput(raw: string) =&amp;gt; ParsedResult (unchanged)
&lt;span class="p"&gt;-&lt;/span&gt; Test gate: all existing tests must pass without modification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then include this in your prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You may ONLY modify src/utils/parser.ts.
Do NOT change the function signature of parseInput().
Do NOT modify any importing files.
If the change requires signature changes, STOP and explain why.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why It Works
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Blast radius is explicit&lt;/strong&gt; — you decide what can change before the AI touches anything&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interface contracts are frozen&lt;/strong&gt; — the AI can refactor internals but can't break callers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test gates catch drift&lt;/strong&gt; — if existing tests need changes, that's a red flag, not a feature&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Real Example
&lt;/h2&gt;

&lt;p&gt;I needed to optimize a token-counting function. Without the firewall, my assistant rewrote it, changed the return type from &lt;code&gt;number&lt;/code&gt; to &lt;code&gt;{ count: number; truncated: boolean }&lt;/code&gt;, and updated four callers. Three of those callers were in a shared library used by two other services.&lt;/p&gt;

&lt;p&gt;With the firewall prompt, the assistant optimized the internals, kept the signature identical, and added the &lt;code&gt;truncated&lt;/code&gt; field as a separate function. Zero blast radius.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Checklist
&lt;/h2&gt;

&lt;p&gt;Before every AI code change:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] List files allowed to change&lt;/li&gt;
&lt;li&gt;[ ] List frozen interfaces/signatures&lt;/li&gt;
&lt;li&gt;[ ] Define test gate (which tests must pass unchanged)&lt;/li&gt;
&lt;li&gt;[ ] Add boundary to your prompt&lt;/li&gt;
&lt;li&gt;[ ] Review the diff against your boundary before merging&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Skip It
&lt;/h2&gt;

&lt;p&gt;For greenfield code with no callers yet, you don't need a firewall. But the moment something has dependents — even one — define the boundary.&lt;/p&gt;

&lt;p&gt;The five minutes you spend writing a change boundary will save you the hour you'd spend debugging a cascade failure. Every time.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>productivity</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My RAG Feature Pipeline Started Simple… Then Got Personal 🤖📦</title>
      <dc:creator>golden Star</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:04:12 +0000</pubDate>
      <link>https://scale.forem.com/tomorrmonkey/my-rag-feature-pipeline-started-simple-then-got-personal-a8k</link>
      <guid>https://scale.forem.com/tomorrmonkey/my-rag-feature-pipeline-started-simple-then-got-personal-a8k</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbnbnc5pm3zudev858jl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftbnbnc5pm3zudev858jl.jpg" alt=" " width="648" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built a RAG feature pipeline thinking it would be clean:&lt;/p&gt;

&lt;p&gt;“Just take raw data, process it, generate embeddings, store in vector DB… done.”&lt;/p&gt;

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

&lt;p&gt;“Done.”&lt;/p&gt;

&lt;p&gt;Step 1: Clean the Data (aka emotional damage)&lt;/p&gt;

&lt;p&gt;I opened my dataset.&lt;/p&gt;

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

&lt;p&gt;broken text&lt;br&gt;
random HTML&lt;br&gt;
sentences that started in 2012 and ended in 2026&lt;/p&gt;

&lt;p&gt;So I cleaned it.&lt;/p&gt;

&lt;p&gt;Then cleaned it again.&lt;/p&gt;

&lt;p&gt;Then realized:&lt;/p&gt;

&lt;p&gt;“Cleaning data is just debugging… but slower.”&lt;/p&gt;

&lt;p&gt;Step 2: Chunking (aka cutting things you don’t understand)&lt;/p&gt;

&lt;p&gt;Now I had to split text into chunks.&lt;/p&gt;

&lt;p&gt;Too big → model confused&lt;br&gt;
Too small → model useless&lt;/p&gt;

&lt;p&gt;So I picked a size and said:&lt;/p&gt;

&lt;p&gt;“Looks reasonable.”&lt;/p&gt;

&lt;p&gt;(It wasn’t.)&lt;/p&gt;

&lt;p&gt;Step 3: Embeddings (aka turning words into math magic)&lt;/p&gt;

&lt;p&gt;I converted text into vectors.&lt;/p&gt;

&lt;p&gt;Thousands of them.&lt;/p&gt;

&lt;p&gt;They looked like:&lt;/p&gt;

&lt;p&gt;[0.123, -0.928, 0.44, …]&lt;/p&gt;

&lt;p&gt;I nodded like I understood.&lt;/p&gt;

&lt;p&gt;I did not.&lt;/p&gt;

&lt;p&gt;Step 4: Store in Vector DB&lt;/p&gt;

&lt;p&gt;Everything went into the database.&lt;/p&gt;

&lt;p&gt;Fast. Scalable. Beautiful.&lt;/p&gt;

&lt;p&gt;Until I queried it.&lt;/p&gt;

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

&lt;p&gt;I asked:&lt;/p&gt;

&lt;p&gt;“Find relevant context.”&lt;/p&gt;

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

&lt;p&gt;Something… technically related.&lt;/p&gt;

&lt;p&gt;Emotionally unrelated.&lt;/p&gt;

&lt;p&gt;Final Lesson&lt;/p&gt;

&lt;p&gt;A RAG pipeline is not:&lt;/p&gt;

&lt;p&gt;just cleaning&lt;br&gt;
just chunking&lt;br&gt;
just embedding&lt;/p&gt;

&lt;p&gt;It’s:&lt;/p&gt;

&lt;p&gt;making sure your future self doesn’t question your life choices.&lt;/p&gt;

&lt;p&gt;Truth&lt;/p&gt;

&lt;p&gt;If your RAG output is bad…&lt;/p&gt;

&lt;p&gt;It’s not the model.&lt;/p&gt;

&lt;p&gt;It’s your pipeline.&lt;/p&gt;

&lt;p&gt;And that’s when I realized:&lt;/p&gt;

&lt;p&gt;I didn’t build a feature pipeline.&lt;/p&gt;

&lt;p&gt;I built a system that politely reflects my bad decisions… at scale.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Why I built a SQLite workbench in bash</title>
      <dc:creator>Allen McCabe</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:03:59 +0000</pubDate>
      <link>https://scale.forem.com/fissible/why-i-built-a-sqlite-workbench-in-bash-3m5o</link>
      <guid>https://scale.forem.com/fissible/why-i-built-a-sqlite-workbench-in-bash-3m5o</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;You SSH into a server. The SQLite database is right there — you can see it in the filesystem.&lt;br&gt;
Every GUI tool you own stops working. TablePlus, DB Browser, Beekeeper — all of them need a local connection. sqlite3 is available, but it's raw SQL with no browsing. litecli is read-biased and still needs installing.&lt;br&gt;
You need to look at some rows, update a field, check an index. You end up writing SELECT statements into a CLI, copying output into a notes file, writing UPDATE statements by hand.&lt;br&gt;
There's a better way.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The build
&lt;/h2&gt;

&lt;p&gt;ShellQL is built on shellframe — a TUI framework I wrote in bash. shellframe handles screen management, keyboard routing, dirty-region rendering, and component lifecycle. Writing a new application on top of it is closer to writing a React app than writing a bash script.&lt;/p&gt;

&lt;p&gt;The surprising parts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mouse support in bash is real, and it's not that hard once you understand xterm escape sequences&lt;/li&gt;
&lt;li&gt;SQLite's &lt;code&gt;.schema&lt;/code&gt; output is parseable enough to build a schema browser without any external tools&lt;/li&gt;
&lt;li&gt;Tab management (multiple tables open simultaneously) required rethinking shellframe's focus model&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The SSH use case
&lt;/h2&gt;

&lt;p&gt;This is the thing that makes ShellQL different from every other SQLite tool.&lt;/p&gt;

&lt;p&gt;If the machine has bash and sqlite3, ShellQL runs. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Production servers (read and write, with care)&lt;/li&gt;
&lt;li&gt;Docker containers&lt;/li&gt;
&lt;li&gt;CI environments for debugging test databases&lt;/li&gt;
&lt;li&gt;Remote dev boxes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No GUI install. No port forwarding. No pulling the file to your laptop and pushing it back.&lt;/p&gt;

&lt;p&gt;SSH in, run &lt;code&gt;shql /var/app/production.db&lt;/code&gt;, browse your data.&lt;/p&gt;




&lt;h2&gt;
  
  
  Full CRUD
&lt;/h2&gt;

&lt;p&gt;Most TUI database tools are read-only. ShellQL isn't.&lt;/p&gt;

&lt;p&gt;The record editor is a schema-aware form overlay. It shows column types and NOT NULL constraints. Tab through fields, edit values, press Enter to submit. Insert new rows the same way.&lt;/p&gt;

&lt;p&gt;Table creation uses a SQL query tab preloaded with a &lt;code&gt;CREATE TABLE&lt;/code&gt; template — you get full DDL control without a rigid GUI wizard.&lt;/p&gt;




&lt;h2&gt;
  
  
  Mouse support
&lt;/h2&gt;

&lt;p&gt;This one surprised people in early demos. Most bash tools are keyboard-only by design. ShellQL supports both.&lt;/p&gt;

&lt;p&gt;Keyboard navigation is fast once you learn it — the keybindings are shown at the bottom of every screen. Mouse works for everything else: clicking into tables, scrolling rows, selecting records.&lt;/p&gt;

&lt;p&gt;This matters for adoption. Not everyone who SSHes into a server is a power user.&lt;/p&gt;




&lt;h2&gt;
  
  
  Install and try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;fissible/tap/shellql
shql my.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Tool page: &lt;a href="https://fissible.dev/tools/shellql" rel="noopener noreferrer"&gt;https://fissible.dev/tools/shellql&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/fissible/shellql" rel="noopener noreferrer"&gt;https://github.com/fissible/shellql&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>database</category>
      <category>tooling</category>
      <category>bash</category>
    </item>
    <item>
      <title>Can We Ever Achieve a Utopian Release?</title>
      <dc:creator>Isabelle M.</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:03:50 +0000</pubDate>
      <link>https://scale.forem.com/trinityyi/can-we-ever-achieve-a-utopian-release-ggf</link>
      <guid>https://scale.forem.com/trinityyi/can-we-ever-achieve-a-utopian-release-ggf</guid>
      <description>&lt;p&gt;What if releases didn't feel stressful?&lt;/p&gt;

&lt;p&gt;Not just "we followed the process" safe, but actually safe. The kind where you don't hesitate before deploying, don't keep checking Slack afterward, and don't quietly wonder what might go wrong this time.&lt;/p&gt;

&lt;p&gt;Because if we're honest, most of us recognize a very specific moment.&lt;/p&gt;

&lt;p&gt;A release finishes. Everything is deployed.&lt;br&gt;
Dashboards are open. People are watching.&lt;/p&gt;

&lt;p&gt;Slack goes a bit quieter than usual.&lt;/p&gt;

&lt;p&gt;No one says anything, but the same thought is there in the background:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Let's see what breaks.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;We spend a lot of time trying to improve releases.&lt;/p&gt;

&lt;p&gt;We add better pipelines. We introduce more automation. We create more environments.&lt;/p&gt;

&lt;p&gt;On paper, things look more mature over time.&lt;br&gt;
And yet, the feeling often doesn't change.&lt;/p&gt;

&lt;p&gt;That's because the real issue is not only technical.&lt;/p&gt;

&lt;p&gt;Releases don't feel risky because they fail. They feel risky because they are unpredictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Myth of the "Perfect Release"
&lt;/h2&gt;

&lt;p&gt;There is a quiet assumption behind many teams: If everything is done correctly, nothing will go wrong.&lt;/p&gt;

&lt;p&gt;It sounds reasonable. It's also not true.&lt;/p&gt;

&lt;p&gt;Systems are complex. Dependencies are not always visible. Production behaves differently. And people make decisions under pressure.&lt;/p&gt;

&lt;p&gt;You don't get perfect releases. You get managed risk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Things Actually Go Wrong
&lt;/h2&gt;

&lt;p&gt;Most issues don't come from the places we expect.&lt;br&gt;
They come from small decisions that feel harmless:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"It's a small change"&lt;/li&gt;
&lt;li&gt;"Let's include this one as well"&lt;/li&gt;
&lt;li&gt;"We don’t need to wait for this"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each decision makes sense on its own. But they don't exist in isolation.&lt;br&gt;
Over time, they accumulate. And the system becomes harder to reason about.&lt;/p&gt;

&lt;p&gt;That's when releases start to feel risky.&lt;/p&gt;




&lt;p&gt;Most teams try to solve this with process and tooling.&lt;/p&gt;

&lt;p&gt;They define what "done" means.&lt;br&gt;
They rely on automation.&lt;br&gt;
They introduce canary releases.&lt;br&gt;
They build dashboards.&lt;/p&gt;

&lt;p&gt;And all of these help. But they also create a dangerous illusion: That if everything looks correct, everything &lt;em&gt;is&lt;/em&gt; correct.&lt;/p&gt;

&lt;p&gt;In reality:&lt;br&gt;
A ticket can be "done" and still hide assumptions.&lt;br&gt;
Tests can pass and still miss real behavior.&lt;br&gt;
A canary can look healthy at 5% and fail at scale.&lt;br&gt;
Dashboards can show green while something important is broken.&lt;/p&gt;

&lt;p&gt;Release problems rarely come from one big mistake. They come from many small ones.&lt;/p&gt;

&lt;p&gt;One extra ticket.&lt;br&gt;
One skipped validation.&lt;br&gt;
One assumption left unchecked.&lt;/p&gt;

&lt;p&gt;Each decision feels reasonable. Together, they create unpredictability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Actually Helps
&lt;/h2&gt;

&lt;p&gt;There's no single fix.&lt;br&gt;
What works is consistency:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only shipping work that is truly ready&lt;/li&gt;
&lt;li&gt;Introducing changes in small steps&lt;/li&gt;
&lt;li&gt;Detecting issues early&lt;/li&gt;
&lt;li&gt;Recovering quickly and calmly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are simple ideas. But they require discipline.&lt;/p&gt;

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

&lt;p&gt;If you want to improve your releases, start here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define what "release-ready" actually means — and stick to it&lt;/li&gt;
&lt;li&gt;Reduce how much you ship at once&lt;/li&gt;
&lt;li&gt;Keep your main branch releasable at all times&lt;/li&gt;
&lt;li&gt;Monitor a few critical user flows closely after deployment&lt;/li&gt;
&lt;li&gt;Make rollback simple and familiar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You don't need to fix everything at once.&lt;br&gt;
Even one improvement can make releases feel more predictable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing Thought
&lt;/h2&gt;

&lt;p&gt;A utopian release is not one where nothing ever goes wrong.&lt;br&gt;
It's one where things can go wrong — and it doesn't turn into chaos.&lt;br&gt;
Where issues are expected, visible, and manageable.&lt;br&gt;
Where releases stop feeling like events and start feeling like routine.&lt;/p&gt;

&lt;p&gt;The goal isn't perfect releases.&lt;br&gt;
It's releases you don’t have to be afraid of.&lt;/p&gt;

&lt;p&gt;That's probably the closest thing to utopia we get.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>productivity</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>PeachBot: Rethinking AI as a Distributed System (Not Another Model)</title>
      <dc:creator>Swapin Vidya</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:03:08 +0000</pubDate>
      <link>https://scale.forem.com/swapin_vidya_426f181a23d9/peachbot-rethinking-ai-as-a-distributed-system-not-another-model-4jda</link>
      <guid>https://scale.forem.com/swapin_vidya_426f181a23d9/peachbot-rethinking-ai-as-a-distributed-system-not-another-model-4jda</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Most AI today is impressive.&lt;br&gt;
Almost none of it works where it actually matters.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  ⚠️ The Moment It Breaks
&lt;/h2&gt;

&lt;p&gt;AI demos are easy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clean datasets&lt;/li&gt;
&lt;li&gt;Stable internet&lt;/li&gt;
&lt;li&gt;Unlimited compute&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything looks great.&lt;/p&gt;

&lt;p&gt;Until you move it into the real world:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A rural clinic with unstable connectivity&lt;/li&gt;
&lt;li&gt;A wetland ecosystem with noisy sensor data&lt;/li&gt;
&lt;li&gt;A farm where conditions change every hour&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And suddenly…&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The “intelligent system” stops being intelligent.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not because the model is bad.&lt;br&gt;
Because the &lt;strong&gt;architecture is wrong&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Uncomfortable Truth
&lt;/h2&gt;

&lt;p&gt;Most AI today is built like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input → model → output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or worse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;input → API → LLM → output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates systems that are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stateless&lt;/li&gt;
&lt;li&gt;Centralized&lt;/li&gt;
&lt;li&gt;Latency-dependent&lt;/li&gt;
&lt;li&gt;Probabilistic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They don’t &lt;em&gt;understand systems&lt;/em&gt;.&lt;br&gt;
They just &lt;em&gt;predict outputs&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Shift We’re Making
&lt;/h2&gt;

&lt;p&gt;We stopped asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“How do we improve models?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And started asking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“What if intelligence isn’t a model at all?”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That question led to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;signals → state → reasoning → decision → feedback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a pipeline.&lt;/p&gt;

&lt;p&gt;It’s a &lt;strong&gt;living system&lt;/strong&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  Enter PeachBot
&lt;/h1&gt;

&lt;p&gt;PeachBot is a &lt;strong&gt;biologically-grounded, edge-native intelligence framework&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Not a wrapper.&lt;br&gt;
Not a model.&lt;br&gt;
Not an API layer.&lt;/p&gt;

&lt;p&gt;A system.&lt;/p&gt;


&lt;h2&gt;
  
  
  ❌ What We Explicitly Avoided
&lt;/h2&gt;

&lt;p&gt;Let’s be clear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No LLMs&lt;/li&gt;
&lt;li&gt;No API orchestration&lt;/li&gt;
&lt;li&gt;No cloud dependency&lt;/li&gt;
&lt;li&gt;No hallucinated outputs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not because they’re bad.&lt;/p&gt;

&lt;p&gt;But because they &lt;strong&gt;don’t solve real-world system problems&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  The Core Idea: Intelligence is State, Not Output
&lt;/h2&gt;

&lt;p&gt;Most AI systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take input&lt;/li&gt;
&lt;li&gt;Produce output&lt;/li&gt;
&lt;li&gt;Forget everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PeachBot systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maintain state&lt;/li&gt;
&lt;li&gt;Continuously update&lt;/li&gt;
&lt;li&gt;Adapt decisions over time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Less like a chatbot&lt;br&gt;
More like a &lt;strong&gt;control system + biological organism&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  ⚙️ Under the Hood (Simplified)
&lt;/h2&gt;

&lt;p&gt;A PeachBot node looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Real-world signals
    ↓
Structured state
    ↓
Knowledge integration
    ↓
State-based reasoning (SBC)
    ↓
Safety validation
    ↓
Action / alert
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And across the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Local intelligence → coordination → emergent global behavior
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  SBC — Synthetic Biological Computation
&lt;/h2&gt;

&lt;p&gt;This is the core shift.&lt;/p&gt;

&lt;p&gt;SBC treats intelligence as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A continuously evolving state machine&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A function call to a model&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This enables:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Context-aware reasoning&lt;/li&gt;
&lt;li&gt;Continuous adaptation&lt;/li&gt;
&lt;li&gt;Deterministic behavior&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  FILA — Distributed Intelligence Layer
&lt;/h2&gt;

&lt;p&gt;Instead of centralizing everything:&lt;/p&gt;

&lt;p&gt;Each node:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sees a partial view&lt;/li&gt;
&lt;li&gt;Learns locally&lt;/li&gt;
&lt;li&gt;Shares structured updates (not raw data)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Privacy-preserving&lt;/li&gt;
&lt;li&gt;Scalable&lt;/li&gt;
&lt;li&gt;Fault-tolerant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is closer to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Distributed systems + biological networks&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;p&gt;Because real-world systems have constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latency is not optional&lt;/li&gt;
&lt;li&gt;Privacy is not negotiable&lt;/li&gt;
&lt;li&gt;Connectivity is not guaranteed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And most AI ignores all three.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where This Is Being Used
&lt;/h2&gt;

&lt;p&gt;This isn’t theoretical.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🏥 Clinical intelligence systems&lt;/li&gt;
&lt;li&gt;🌊 Environmental monitoring (live deployments)&lt;/li&gt;
&lt;li&gt;🌾 Precision agriculture&lt;/li&gt;
&lt;li&gt;🧬 Biological modeling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are environments where:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Almost working” = failing.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Bigger Realization
&lt;/h2&gt;

&lt;p&gt;We didn’t just build a new system.&lt;/p&gt;

&lt;p&gt;We realized something uncomfortable:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI is being treated as a feature.&lt;br&gt;
It should be treated as infrastructure.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  This Is an Open System
&lt;/h2&gt;

&lt;p&gt;We’re building this as a modular ecosystem:&lt;/p&gt;

&lt;p&gt;👉&lt;a href="https://github.com/peachbotAI" rel="noopener noreferrer"&gt;https://github.com/peachbotAI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Core layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SBC (state-centric computation)&lt;/li&gt;
&lt;li&gt;Knowledge graphs&lt;/li&gt;
&lt;li&gt;Edge runtime&lt;/li&gt;
&lt;li&gt;FILA coordination&lt;/li&gt;
&lt;li&gt;Deployment stack&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  We Need Builders (Not Just Users)
&lt;/h1&gt;

&lt;p&gt;If this resonates, you’re probably not here for tutorials.&lt;/p&gt;

&lt;p&gt;You’re here to build.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where You Can Contribute
&lt;/h2&gt;

&lt;p&gt;We’re actively looking for people interested in:&lt;/p&gt;

&lt;h3&gt;
  
  
  Systems &amp;amp; Infrastructure
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Distributed systems&lt;/li&gt;
&lt;li&gt;Protocol design&lt;/li&gt;
&lt;li&gt;Edge orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Intelligence Layer
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Knowledge graphs&lt;/li&gt;
&lt;li&gt;Hybrid reasoning systems&lt;/li&gt;
&lt;li&gt;Graph-based models (GNNs)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Engineering
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Embedded / edge systems&lt;/li&gt;
&lt;li&gt;Performance optimization&lt;/li&gt;
&lt;li&gt;Real-time pipelines&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Safety &amp;amp; Validation
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Deterministic validation layers&lt;/li&gt;
&lt;li&gt;Policy enforcement&lt;/li&gt;
&lt;li&gt;Risk systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Who This Is For
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Engineers tired of building wrappers&lt;/li&gt;
&lt;li&gt;Researchers questioning model-centric AI&lt;/li&gt;
&lt;li&gt;Builders interested in real-world systems&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What We Want Feedback On
&lt;/h2&gt;

&lt;p&gt;We’re still early.&lt;/p&gt;

&lt;p&gt;We’d love input on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SBC as a computation paradigm&lt;/li&gt;
&lt;li&gt;FILA as a distributed cognition model&lt;/li&gt;
&lt;li&gt;Real-world deployment constraints&lt;/li&gt;
&lt;li&gt;Developer experience&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔗 Dive Deeper
&lt;/h2&gt;

&lt;p&gt;👉 Full blog:&lt;br&gt;
&lt;a href="https://peachbot.in/blogs/peachbot-the-future-of-edge-ai-biologically-grounded-intelligence-at-the-source" rel="noopener noreferrer"&gt;https://peachbot.in/blogs/peachbot-the-future-of-edge-ai-biologically-grounded-intelligence-at-the-source&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 GitHub:&lt;br&gt;
&lt;a href="https://github.com/peachbotAI" rel="noopener noreferrer"&gt;https://github.com/peachbotAI&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;We don’t need bigger models.&lt;/p&gt;

&lt;p&gt;We need better systems.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Not AI that talks.&lt;br&gt;
But AI that &lt;strong&gt;operates&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ai</category>
      <category>edge</category>
      <category>distributedsystems</category>
      <category>opensource</category>
    </item>
    <item>
      <title>24 JavaScript Code Analysis Tools You Should Know</title>
      <dc:creator>Rahul Singh</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:00:00 +0000</pubDate>
      <link>https://scale.forem.com/rahulxsingh/24-javascript-code-analysis-tools-you-should-know-28a3</link>
      <guid>https://scale.forem.com/rahulxsingh/24-javascript-code-analysis-tools-you-should-know-28a3</guid>
      <description>&lt;h2&gt;
  
  
  Why JavaScript code analysis matters
&lt;/h2&gt;

&lt;p&gt;JavaScript is the most widely used programming language in the world, powering everything from simple landing pages to complex distributed systems running on Node.js and Deno. It is also one of the most permissive. The language happily lets you compare a string to an array, access properties on &lt;code&gt;undefined&lt;/code&gt;, mutate global state from anywhere, and silently coerce types in ways that produce runtime bugs invisible to the naked eye.&lt;/p&gt;

&lt;p&gt;This permissiveness is why JavaScript code analysis is not optional - it is essential. Unlike statically typed languages where the compiler catches type mismatches and missing imports before your code ever runs, JavaScript defers almost everything to runtime. A typo in a variable name does not fail to compile. A function that sometimes returns a number and sometimes returns &lt;code&gt;null&lt;/code&gt; does not raise an error. A missing &lt;code&gt;await&lt;/code&gt; on a Promise does not crash your build.&lt;/p&gt;

&lt;p&gt;Static analysis tools fill this gap. They read your code without executing it and flag problems that would otherwise surface as production bugs, security vulnerabilities, or performance regressions. The JavaScript analysis ecosystem has matured dramatically: in 2026, you can lint, format, bundle-analyze, security-scan, and AI-review your code with tools that range from zero-config formatters to enterprise SAST platforms.&lt;/p&gt;

&lt;p&gt;This guide covers 24 tools across six categories. Whether you are a solo developer setting up a side project or an engineering lead standardizing tooling for a 200-person team, you will find the right combination here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison table: all 24 tools at a glance
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Pricing&lt;/th&gt;
&lt;th&gt;Languages&lt;/th&gt;
&lt;th&gt;IDE Support&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;ESLint&lt;/td&gt;
&lt;td&gt;Linter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS, JSX, TSX&lt;/td&gt;
&lt;td&gt;VS Code, JetBrains, Vim, Neovim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Biome&lt;/td&gt;
&lt;td&gt;Linter + Formatter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS, JSX, TSX, JSON, CSS&lt;/td&gt;
&lt;td&gt;VS Code, JetBrains, Neovim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;oxlint&lt;/td&gt;
&lt;td&gt;Linter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS, JSX, TSX&lt;/td&gt;
&lt;td&gt;VS Code (via extension)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;typescript-eslint&lt;/td&gt;
&lt;td&gt;Linter (TS plugin)&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;TypeScript&lt;/td&gt;
&lt;td&gt;Via ESLint IDE support&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;Linter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS&lt;/td&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;XO&lt;/td&gt;
&lt;td&gt;Linter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS&lt;/td&gt;
&lt;td&gt;VS Code, Vim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Prettier&lt;/td&gt;
&lt;td&gt;Formatter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS, HTML, CSS, JSON, MD, + more&lt;/td&gt;
&lt;td&gt;VS Code, JetBrains, Vim, Neovim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;dprint&lt;/td&gt;
&lt;td&gt;Formatter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS, JSON, MD, HTML, CSS&lt;/td&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;Biome Formatter&lt;/td&gt;
&lt;td&gt;Formatter&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;JS, TS, JSX, TSX, JSON, CSS&lt;/td&gt;
&lt;td&gt;VS Code, JetBrains, Neovim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;webpack-bundle-analyzer&lt;/td&gt;
&lt;td&gt;Bundler Analyzer&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;Any (webpack)&lt;/td&gt;
&lt;td&gt;N/A (browser-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;source-map-explorer&lt;/td&gt;
&lt;td&gt;Bundler Analyzer&lt;/td&gt;
&lt;td&gt;Free (OSS)&lt;/td&gt;
&lt;td&gt;Any (source maps)&lt;/td&gt;
&lt;td&gt;N/A (CLI + browser)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;bundlephobia&lt;/td&gt;
&lt;td&gt;Bundler Analyzer&lt;/td&gt;
&lt;td&gt;Free (web)&lt;/td&gt;
&lt;td&gt;npm packages&lt;/td&gt;
&lt;td&gt;N/A (web-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/semgrep/"&gt;Semgrep&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Security Scanner&lt;/td&gt;
&lt;td&gt;Free / $35/contributor/mo&lt;/td&gt;
&lt;td&gt;30+ including JS, TS&lt;/td&gt;
&lt;td&gt;VS Code, JetBrains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/snyk-code/"&gt;Snyk Code&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Security Scanner&lt;/td&gt;
&lt;td&gt;Free (limited) / $25/dev/mo&lt;/td&gt;
&lt;td&gt;19+ including JS, TS&lt;/td&gt;
&lt;td&gt;VS Code, JetBrains, Visual Studio&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;npm audit&lt;/td&gt;
&lt;td&gt;Security Scanner&lt;/td&gt;
&lt;td&gt;Free (built-in)&lt;/td&gt;
&lt;td&gt;npm packages&lt;/td&gt;
&lt;td&gt;N/A (CLI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;Socket&lt;/td&gt;
&lt;td&gt;Security Scanner&lt;/td&gt;
&lt;td&gt;Free (OSS) / paid plans&lt;/td&gt;
&lt;td&gt;npm, PyPI&lt;/td&gt;
&lt;td&gt;GitHub app&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/sonarqube/"&gt;SonarQube&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Code Quality Platform&lt;/td&gt;
&lt;td&gt;Free (Community) / ~$2,500/yr&lt;/td&gt;
&lt;td&gt;35+ including JS, TS&lt;/td&gt;
&lt;td&gt;VS Code (SonarLint), JetBrains&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;CodeClimate&lt;/td&gt;
&lt;td&gt;Code Quality Platform&lt;/td&gt;
&lt;td&gt;Free (OSS) / $600+/mo&lt;/td&gt;
&lt;td&gt;22+ including JS, TS&lt;/td&gt;
&lt;td&gt;N/A (CI-based)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/deepsource/"&gt;DeepSource&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Code Quality Platform&lt;/td&gt;
&lt;td&gt;Free (individual) / $12/user/mo&lt;/td&gt;
&lt;td&gt;16 including JS, TS&lt;/td&gt;
&lt;td&gt;VS Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/codacy/"&gt;Codacy&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Code Quality Platform&lt;/td&gt;
&lt;td&gt;Free (limited) / $15/user/mo&lt;/td&gt;
&lt;td&gt;49 including JS, TS&lt;/td&gt;
&lt;td&gt;VS Code (AI Guardrails)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/coderabbit/"&gt;CodeRabbit&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;AI Code Reviewer&lt;/td&gt;
&lt;td&gt;Free (unlimited) / $24/user/mo&lt;/td&gt;
&lt;td&gt;30+ including JS, TS&lt;/td&gt;
&lt;td&gt;GitHub, GitLab, Azure DevOps, Bitbucket&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;CodeAnt AI&lt;/td&gt;
&lt;td&gt;AI Code Reviewer&lt;/td&gt;
&lt;td&gt;Free (Basic) / $24/user/mo&lt;/td&gt;
&lt;td&gt;30+ including JS, TS&lt;/td&gt;
&lt;td&gt;GitHub, GitLab, Bitbucket, Azure DevOps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;GitHub Copilot Code Review&lt;/td&gt;
&lt;td&gt;AI Code Reviewer&lt;/td&gt;
&lt;td&gt;$19/user/mo (with Copilot)&lt;/td&gt;
&lt;td&gt;All GitHub languages&lt;/td&gt;
&lt;td&gt;GitHub (native)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;&lt;a href="https://dev.to/tool/sourcery/"&gt;Sourcery&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;AI Code Reviewer&lt;/td&gt;
&lt;td&gt;Free (OSS) / $10/user/mo&lt;/td&gt;
&lt;td&gt;JS, TS, Python, Go&lt;/td&gt;
&lt;td&gt;GitHub, GitLab, VS Code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Linters
&lt;/h2&gt;

&lt;p&gt;Linters are the foundation of any JavaScript code analysis pipeline. They parse your source code into an abstract syntax tree and check it against a set of rules that catch bugs, enforce coding conventions, and prevent anti-patterns. Every JavaScript project should have a linter. The only question is which one.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. ESLint - The industry standard
&lt;/h3&gt;

&lt;p&gt;ESLint is the most widely used JavaScript linter, with over 30 million weekly downloads on npm and an ecosystem of thousands of plugins. If you have worked on a JavaScript project in the last decade, you have used ESLint. It remains the default choice in 2026, though the landscape around it has changed significantly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What changed with v9.&lt;/strong&gt; ESLint v9, released in 2024, introduced the flat config system - a single &lt;code&gt;eslint.config.js&lt;/code&gt; file that replaces the old &lt;code&gt;.eslintrc.*&lt;/code&gt; cascade. The flat config is simpler, more predictable, and eliminates the confusion of config inheritance across nested directories. The migration from legacy config to flat config is the biggest thing teams need to handle, and the eslint.org migration guide covers it well.&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 (flat config)&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;js&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;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="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="s2"&gt;no-unused-vars&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="s2"&gt;error&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="s2"&gt;no-console&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="s2"&gt;warn&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="s2"&gt;prefer-const&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="s2"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;ignores&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="s2"&gt;dist/&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="s2"&gt;node_modules/&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="s2"&gt;*.config.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Over 200 built-in rules. Thousands of community plugins covering React (&lt;code&gt;eslint-plugin-react&lt;/code&gt;, &lt;code&gt;eslint-plugin-react-hooks&lt;/code&gt;), accessibility (&lt;code&gt;eslint-plugin-jsx-a11y&lt;/code&gt;), import management (&lt;code&gt;eslint-plugin-import&lt;/code&gt;), and framework-specific patterns. Autofix support for many rules. The &lt;code&gt;--fix&lt;/code&gt; flag resolves fixable issues automatically on save or in CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; ESLint is the right choice for any project that needs extensive customization, has an existing ESLint configuration, or relies on framework-specific plugins. If your project uses React, Vue, Svelte, Angular, or any major framework, the ESLint plugin ecosystem is unmatched.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Biome - The Rust-powered successor to Rome
&lt;/h3&gt;

&lt;p&gt;Biome is the community fork and successor to Rome, the ambitious all-in-one JavaScript toolchain project. Written in Rust, Biome combines linting and formatting into a single tool that runs 20-35x faster than ESLint on most codebases. It has rapidly grown from experimental to production-ready, and in 2026 it is the strongest challenger to the ESLint + Prettier combination.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it fast.&lt;/strong&gt; Biome parses your code once and runs both lint rules and formatting in a single pass. ESLint and Prettier each parse your code separately, which means the ESLint + Prettier combo parses every file twice. Biome's Rust implementation also avoids the startup overhead of Node.js. On a 100,000-line codebase, Biome typically completes in 200-500ms where ESLint alone takes 8-15 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;biome.json&lt;/span&gt;&lt;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;"$schema"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://biomejs.dev/schemas/1.9.4/schema.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"complexity"&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;"noForEach"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"warn"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"suspicious"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"noExplicitAny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"indentStyle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"space"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"indentWidth"&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="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Over 270 lint rules with strong TypeScript, React, and accessibility coverage. Built-in formatter that is 97%+ compatible with Prettier output. Import sorting. JSON and CSS support. Zero configuration needed for most projects - &lt;code&gt;npx @biomejs/biome init&lt;/code&gt; generates a sensible default config.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Biome is the best choice for new projects that want fast linting and formatting without managing two tools. For existing projects with complex ESLint configurations, migration effort depends on how many ESLint plugins you rely on - Biome covers the most common rules but does not replicate every niche plugin.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. oxlint - The fastest pure linter
&lt;/h3&gt;

&lt;p&gt;oxlint is part of the Oxidation Compiler (oxc) project, a collection of Rust-based JavaScript tools built for raw speed. While Biome aims to be an all-in-one toolchain, oxlint focuses exclusively on linting and pushes performance to the extreme. It is designed to complement ESLint, not replace it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How fast is it.&lt;/strong&gt; oxlint is 50-100x faster than ESLint on benchmarks. On a large monorepo, oxlint can complete a full lint pass in under 100ms. This speed makes it practical to run on every keystroke in the IDE without any perceptible delay.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Over 400 rules ported from ESLint, typescript-eslint, eslint-plugin-react, eslint-plugin-jest, and eslint-plugin-import. Cross-file analysis support. Automatic detection of existing ESLint configs to avoid duplicate warnings. Designed to run alongside ESLint - oxlint handles the rules it supports, and ESLint handles the rest.&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;# Run oxlint on your project&lt;/span&gt;
npx oxlint@latest

&lt;span class="c"&gt;# With specific rules&lt;/span&gt;
npx oxlint@latest &lt;span class="nt"&gt;--deny-warnings&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; correctness
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; oxlint is ideal as a fast first-pass linter that catches the most common issues instantly, with ESLint running behind it for framework-specific rules that oxlint does not yet cover. As the rule library grows, it may become a standalone ESLint replacement for some projects.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. typescript-eslint - Type-aware linting for TypeScript
&lt;/h3&gt;

&lt;p&gt;typescript-eslint is the official ESLint plugin for TypeScript. It bridges ESLint's rule-based analysis with TypeScript's type system, enabling lint rules that reason about types - something ESLint alone cannot do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why type-aware rules matter.&lt;/strong&gt; Standard ESLint rules operate on the abstract syntax tree without understanding types. They can tell you that a variable is unused, but they cannot tell you that a function returns &lt;code&gt;Promise&amp;lt;string&amp;gt;&lt;/code&gt; and you forgot to &lt;code&gt;await&lt;/code&gt; it. Type-aware rules from typescript-eslint use the TypeScript compiler's type information to catch this class of bugs.&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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;tseslint&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&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;recommendedTypeChecked&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;parserOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;projectService&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="na"&gt;tsconfigRootDir&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;dirname&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="s2"&gt;@typescript-eslint/no-floating-promises&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="s2"&gt;error&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="s2"&gt;@typescript-eslint/no-misused-promises&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="s2"&gt;error&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="s2"&gt;@typescript-eslint/await-thenable&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="s2"&gt;error&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="s2"&gt;@typescript-eslint/no-unnecessary-type-assertion&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="s2"&gt;warn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Over 100 TypeScript-specific rules. Type-aware rules that catch floating promises, misused async functions, unnecessary type assertions, and unsafe &lt;code&gt;any&lt;/code&gt; usage. Full compatibility with ESLint's plugin ecosystem. The &lt;code&gt;recommendedTypeChecked&lt;/code&gt; config provides a battle-tested set of rules for most TypeScript projects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Every TypeScript project should use typescript-eslint. The type-aware rules catch a category of bugs that no other linter can detect. The performance cost of type-checking is real (slower than non-type-aware linting), but the bugs it catches justify the trade-off.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Standard - Zero-config JavaScript style
&lt;/h3&gt;

&lt;p&gt;Standard (standardjs) is a JavaScript linter and style guide that requires zero configuration. You install it, run it, and it enforces a fixed set of rules with no config files, no rule arguments, and no debates about semicolons. The answer is always: no semicolons, 2-space indentation, single quotes.&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;# Install and run&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;standard &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
npx standard

&lt;span class="c"&gt;# Autofix&lt;/span&gt;
npx standard &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Zero config - no &lt;code&gt;.eslintrc&lt;/code&gt;, no &lt;code&gt;biome.json&lt;/code&gt;, nothing. Catches common bugs alongside style enforcement. Built on ESLint internally, so the underlying analysis is battle-tested. Used by companies like npm, GitHub, and Express.js.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Standard is best for small projects, prototypes, and open-source libraries where you want consistent style without spending time debating lint rules. If your team has strong opinions about formatting (semicolons, tabs vs spaces), Standard is not for you - the entire point is that these decisions are made for you.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. XO - Opinionated ESLint wrapper
&lt;/h3&gt;

&lt;p&gt;XO is an opinionated ESLint wrapper maintained by Sindre Sorhus. It bundles ESLint with a curated set of plugins and rules, providing a more opinionated and maintained alternative to Standard. Where Standard is minimal, XO is comprehensive.&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;# Install and run&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;xo &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
npx xo

&lt;span class="c"&gt;# Autofix&lt;/span&gt;
npx xo &lt;span class="nt"&gt;--fix&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Built-in TypeScript support without additional config. Includes popular plugins like &lt;code&gt;eslint-plugin-unicorn&lt;/code&gt;, &lt;code&gt;eslint-plugin-import&lt;/code&gt;, and &lt;code&gt;eslint-plugin-n&lt;/code&gt; by default. Prettier integration - XO detects Prettier and disables conflicting rules automatically. Opinionated defaults that cover more edge cases than Standard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; XO is ideal for developers who want a stricter, more comprehensive linting experience than Standard but without the configuration overhead of building their own ESLint config from scratch. It is particularly popular in the Sindre Sorhus ecosystem of npm packages.&lt;/p&gt;




&lt;h2&gt;
  
  
  Formatters
&lt;/h2&gt;

&lt;p&gt;Formatters handle code style: indentation, line breaks, semicolons, quote style, trailing commas, and every other aspect of how your code looks on screen. Unlike linters, formatters do not find bugs - they make code consistently readable. The modern consensus is to use a separate formatter rather than relying on your linter for stylistic rules.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Prettier - The standard code formatter
&lt;/h3&gt;

&lt;p&gt;Prettier is the most widely adopted code formatter in the JavaScript ecosystem. It takes an opinionated approach: you configure a few options (print width, tab width, semicolons, quote style) and Prettier handles everything else. The key design principle is that Prettier's output is deterministic - two developers formatting the same code will always get the same result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.prettierrc&lt;/span&gt;&lt;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;"printWidth"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tabWidth"&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;"useTabs"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"semi"&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;"singleQuote"&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;"trailingComma"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"all"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"bracketSpacing"&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;&lt;strong&gt;Key features.&lt;/strong&gt; Supports JavaScript, TypeScript, JSX, TSX, CSS, SCSS, HTML, JSON, Markdown, YAML, GraphQL, and more. Deterministic output - eliminates style debates in code review. Integrates with every major editor and CI system. The &lt;code&gt;--check&lt;/code&gt; flag validates formatting in CI without modifying files. Over 40 million weekly npm downloads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Prettier is the right formatter for any team that wants to eliminate formatting discussions from code reviews. If you use ESLint, pair Prettier with &lt;code&gt;eslint-config-prettier&lt;/code&gt; to disable ESLint's formatting rules and let Prettier handle style. If you use Biome, its built-in formatter replaces Prettier.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. dprint - The Rust-based Prettier alternative
&lt;/h3&gt;

&lt;p&gt;dprint is a code formatter written in Rust that offers Prettier-like formatting with significantly better performance. It formats JavaScript, TypeScript, JSON, Markdown, HTML, and CSS with configurable rules and a plugin architecture.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dprint.json&lt;/span&gt;&lt;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;"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;"quoteStyle"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"preferSingle"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"semiColons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"always"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"trailingCommas"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"onlyMultiLine"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"indentWidth"&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="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;"json"&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;"markdown"&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;"plugins"&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;"https://plugins.dprint.dev/typescript-0.93.3.wasm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://plugins.dprint.dev/json-0.19.4.wasm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://plugins.dprint.dev/markdown-0.17.8.wasm"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; 10-30x faster than Prettier on most codebases. Plugin-based architecture - plugins are WebAssembly binaries that run in a sandboxed environment. More configuration options than Prettier for teams that want fine-grained control. Incremental formatting - only reformats changed lines.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; dprint is the right choice for teams that find Prettier too slow (common in large monorepos) or too opinionated (Prettier intentionally limits configuration options). The plugin architecture means you can mix and match formatters for different file types.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Biome Formatter - Built into Biome, Prettier-compatible
&lt;/h3&gt;

&lt;p&gt;Biome's built-in formatter is 97%+ compatible with Prettier's output, making it a drop-in replacement for most projects. Since it runs alongside Biome's linter in the same pass, you get both linting and formatting from a single tool invocation.&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;# Format files with Biome&lt;/span&gt;
npx @biomejs/biome format &lt;span class="nt"&gt;--write&lt;/span&gt; ./src

&lt;span class="c"&gt;# Check formatting without modifying (CI mode)&lt;/span&gt;
npx @biomejs/biome format ./src
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Near-identical output to Prettier. Runs in the same pass as linting - no double-parsing overhead. Configured in the same &lt;code&gt;biome.json&lt;/code&gt; file as lint rules. Supports JavaScript, TypeScript, JSX, TSX, JSON, and CSS. The &lt;code&gt;biome migrate prettier&lt;/code&gt; command auto-converts a &lt;code&gt;.prettierrc&lt;/code&gt; to &lt;code&gt;biome.json&lt;/code&gt; settings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; If you are already using Biome for linting, use its built-in formatter instead of adding Prettier as a separate tool. The single-tool approach is faster and simpler. If you are on ESLint and considering a switch, the Prettier-compatible output means the migration will not cause massive diffs in your codebase.&lt;/p&gt;




&lt;h2&gt;
  
  
  Bundler analyzers
&lt;/h2&gt;

&lt;p&gt;JavaScript applications are shipped as bundles - compiled, minified files that combine your source code with dependencies. Bundle size directly impacts page load times, user experience, and Core Web Vitals scores. Bundler analyzers help you understand what is in your bundles, how big each dependency is, and where to cut bloat.&lt;/p&gt;

&lt;h3&gt;
  
  
  10. webpack-bundle-analyzer - Interactive treemap visualization
&lt;/h3&gt;

&lt;p&gt;webpack-bundle-analyzer generates an interactive zoomable treemap of your webpack bundles. Each rectangle represents a module, sized proportionally to its contribution to the bundle. It is the fastest way to visually identify which dependencies are consuming the most space.&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;// webpack.config.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BundleAnalyzerPlugin&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="s2"&gt;webpack-bundle-analyzer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;BundleAnalyzerPlugin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BundleAnalyzerPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;analyzerMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;static&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// generates HTML file&lt;/span&gt;
      &lt;span class="na"&gt;openAnalyzer&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="na"&gt;reportFilename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bundle-report.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Interactive treemap opens in the browser. Shows parsed size, gzip size, and stat size for every module. Supports tree-shaking visualization - see what was eliminated and what was not. Works with webpack 4 and 5. The &lt;code&gt;static&lt;/code&gt; mode generates a standalone HTML report you can share with your team or archive in CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under MIT license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Use webpack-bundle-analyzer whenever you suspect your bundle is too large. Run it after adding new dependencies, during performance audits, or on a schedule in CI to track bundle size trends. If you are not using webpack, see source-map-explorer for a bundler-agnostic alternative.&lt;/p&gt;




&lt;h3&gt;
  
  
  11. source-map-explorer - Bundler-agnostic bundle analysis
&lt;/h3&gt;

&lt;p&gt;source-map-explorer analyzes JavaScript bundles using source maps to determine exactly which source files contribute to your final output. Unlike webpack-bundle-analyzer, it works with any bundler that produces source maps - webpack, Vite, Rollup, esbuild, Parcel, and Next.js.&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;# Analyze a production bundle&lt;/span&gt;
npx source-map-explorer dist/main.js

&lt;span class="c"&gt;# Output as HTML report&lt;/span&gt;
npx source-map-explorer dist/main.js &lt;span class="nt"&gt;--html&lt;/span&gt; report.html

&lt;span class="c"&gt;# Analyze multiple bundles&lt;/span&gt;
npx source-map-explorer dist/&lt;span class="k"&gt;*&lt;/span&gt;.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Works with any bundler that generates source maps. Interactive treemap visualization. Shows byte-level attribution of every source file. Detects duplicate modules included multiple times. CLI-based with HTML, JSON, and TSV output options for CI integration.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free and open source under Apache-2.0 license.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; source-map-explorer is the best choice for projects using Vite, Rollup, esbuild, or any non-webpack bundler. It is also useful for debugging production bundles where you only have the compiled output and source maps - common when auditing third-party applications or investigating performance issues in deployed code.&lt;/p&gt;




&lt;h3&gt;
  
  
  12. bundlephobia - Check package size before installing
&lt;/h3&gt;

&lt;p&gt;bundlephobia is a web tool that shows you the cost of adding an npm package to your bundle before you install it. Enter a package name and you immediately see its minified size, gzipped size, download time on 3G/4G, composition breakdown, and whether it supports tree-shaking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Instant size lookup for any npm package. Shows the full dependency tree and which transitive dependencies contribute the most weight. Compares package sizes side-by-side. Export cost analysis shows the size of individual named exports. Scan your &lt;code&gt;package.json&lt;/code&gt; to audit all dependencies at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free web tool.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Check bundlephobia before adding any new dependency. A 2KB utility library and a 200KB utility library might have the same API, but only one belongs in a performance-sensitive frontend bundle. Bundlephobia is also invaluable when choosing between competing packages - &lt;code&gt;date-fns&lt;/code&gt; (16KB tree-shakeable) vs &lt;code&gt;moment&lt;/code&gt; (72KB non-tree-shakeable), for example.&lt;/p&gt;




&lt;h2&gt;
  
  
  Security scanners
&lt;/h2&gt;

&lt;p&gt;JavaScript applications face a unique threat surface. The npm ecosystem has over 2 million packages, and supply chain attacks are increasingly common. Client-side JavaScript is exposed to XSS, CSRF, and injection attacks. Server-side Node.js code handles user input, database queries, and file system access. Security scanners detect vulnerabilities in both your code and your dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. Semgrep - Rule-based SAST with 1,000+ JavaScript rules
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/semgrep/"&gt;Semgrep&lt;/a&gt; is an open-source static analysis tool that uses pattern-matching rules written in a syntax that mirrors the target language. For JavaScript and TypeScript, Semgrep has over 1,000 community rules covering XSS, SQL injection, prototype pollution, NoSQL injection, command injection, path traversal, insecure deserialization, and framework-specific vulnerabilities for Express, React, Next.js, Angular, and Vue.&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="c1"&gt;# Custom Semgrep rule for Express.js&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;express-sql-injection&lt;/span&gt;
    &lt;span class="na"&gt;patterns&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;$DB.query($QUERY + $INPUT, ...)&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pattern-inside&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;function $FUNC($REQ, $RES, ...) { ... }&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="s"&gt;SQL query built with string concatenation using request input.&lt;/span&gt;
      &lt;span class="s"&gt;Use parameterized queries instead.&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ERROR&lt;/span&gt;
    &lt;span class="na"&gt;languages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;javascript&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;typescript&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Over 3,000 total rules across 30+ languages, with 1,000+ covering JavaScript and TypeScript. The rule syntax mirrors JavaScript, making custom rules intuitive to write. Scans complete in seconds - full project scans typically run in 8-15 seconds. The Pro tier adds cross-file taint analysis that traces user input from Express &lt;code&gt;req&lt;/code&gt; objects through middleware and service layers to dangerous sinks. Semgrep Assistant uses AI to triage findings and reduce false positives by up to 60%.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; OSS CLI is free (LGPL-2.1). Full platform is free for up to 10 contributors. Team tier is $35/contributor/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Semgrep is the best security scanner for JavaScript teams that want customizable, fast, CI-friendly scanning. Its rule syntax is developer-friendly enough that frontend engineers can write custom rules for their own frameworks and libraries. For deeper analysis, see our &lt;a href="https://dev.to/blog/how-to-setup-semgrep/"&gt;Semgrep setup guide&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  14. Snyk Code - AI-powered cross-file security analysis
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/snyk-code/"&gt;Snyk Code&lt;/a&gt; is the SAST component of the Snyk platform. Its DeepCode AI engine performs interfile dataflow analysis, tracing the path of user input across multiple files to detect vulnerabilities that single-file scanners miss entirely. For JavaScript and TypeScript projects, this means catching prototype pollution through recursive merge utilities, XSS through React &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; fed by unsanitized API responses, and NoSQL injection through Mongoose query builders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Real-time scanning in VS Code and JetBrains - vulnerabilities appear as you type. Cross-file taint analysis that follows data from &lt;code&gt;req.body&lt;/code&gt; through service layers to database queries. AI-powered fix suggestions with data flow visualizations showing exactly how tainted data reaches a dangerous sink. The broader Snyk platform adds SCA (dependency scanning), container security, and IaC scanning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free tier for 1 user with limited scans. Team plan at $25/dev/month. Enterprise pricing is custom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Snyk Code is the best choice for JavaScript teams that want IDE-integrated security scanning with the deepest cross-file analysis available. It is particularly strong for Node.js backend applications where vulnerabilities often span request handlers, middleware, and data access layers. For setup instructions, see our &lt;a href="https://dev.to/blog/how-to-setup-snyk/"&gt;Snyk setup guide&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  15. npm audit - Built-in dependency vulnerability scanning
&lt;/h3&gt;

&lt;p&gt;npm audit is npm's built-in tool for checking your dependency tree against the GitHub Advisory Database. It scans your &lt;code&gt;package-lock.json&lt;/code&gt; and reports known vulnerabilities in both direct and transitive dependencies.&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;# Check for vulnerabilities&lt;/span&gt;
npm audit

&lt;span class="c"&gt;# Only show high and critical severity&lt;/span&gt;
npm audit &lt;span class="nt"&gt;--audit-level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;high

&lt;span class="c"&gt;# Automatically fix vulnerabilities where possible&lt;/span&gt;
npm audit fix

&lt;span class="c"&gt;# Generate a JSON report for CI&lt;/span&gt;
npm audit &lt;span class="nt"&gt;--json&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; audit-report.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Built into npm - no additional installation required. Covers the entire dependency tree, including transitive dependencies. Shows severity ratings (low, moderate, high, critical), affected versions, and patched versions. The &lt;code&gt;npm audit fix&lt;/code&gt; command automatically updates packages to patched versions when semver-compatible updates exist. The &lt;code&gt;npm audit signatures&lt;/code&gt; command verifies package integrity against registry signatures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free, built into npm.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Run &lt;code&gt;npm audit&lt;/code&gt; in CI on every build. It catches known dependency vulnerabilities with zero configuration. However, npm audit only covers known CVEs in published packages - it does not analyze your own source code for vulnerabilities. Pair it with a SAST tool like Semgrep or Snyk Code for comprehensive security coverage.&lt;/p&gt;




&lt;h3&gt;
  
  
  16. Socket - Supply chain security and malicious package detection
&lt;/h3&gt;

&lt;p&gt;Socket takes a fundamentally different approach to npm security. Instead of matching packages against a CVE database (like npm audit), Socket analyzes the actual behavior of packages to detect supply chain attacks - malicious code, typosquatting, install scripts that exfiltrate data, and packages that suddenly add network access or file system operations in a patch update.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Behavioral analysis of npm packages - detects when a package adds unexpected network requests, file system access, or shell execution in new versions. Typosquatting detection - warns when a package name is suspiciously similar to a popular package. Install script analysis - flags &lt;code&gt;postinstall&lt;/code&gt; scripts that execute arbitrary code. GitHub PR integration - Socket comments on pull requests when new dependencies introduce risk indicators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free for open-source projects. Paid plans for private repositories start at team tiers. Enterprise pricing is custom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Socket is essential for teams that install npm packages frequently and want protection against supply chain attacks that CVE databases do not cover. It is complementary to npm audit: npm audit catches known vulnerabilities, Socket catches malicious behavior. Use both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code quality platforms
&lt;/h2&gt;

&lt;p&gt;Code quality platforms go beyond single-file linting to provide project-wide analysis, technical debt tracking, code coverage integration, and quality gate enforcement. They run in CI/CD pipelines and provide dashboards that track code health over time.&lt;/p&gt;

&lt;h3&gt;
  
  
  17. SonarQube - Enterprise code quality with 300+ JavaScript rules
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/sonarqube/"&gt;SonarQube&lt;/a&gt; is the most widely deployed code quality platform in enterprise environments. For JavaScript and TypeScript, it provides over 300 rules covering bugs, code smells, security vulnerabilities, and security hotspots. Its quality gate system lets you define pass/fail criteria for every pull request and block merges that do not meet your standards.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Over 300 JavaScript/TypeScript rules across bugs, code smells, vulnerabilities, and security hotspots. Quality gate enforcement - block PRs that introduce critical issues, drop below coverage thresholds, or exceed duplication limits. Technical debt tracking with trend visualization over weeks and months. Compliance mapping to OWASP Top 10, CWE, and SANS Top 25. SonarLint IDE extension provides real-time feedback in VS Code and JetBrains with connected mode for shared rule configuration.&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="c1"&gt;# sonar-project.properties&lt;/span&gt;
&lt;span class="s"&gt;sonar.projectKey=my-javascript-project&lt;/span&gt;
&lt;span class="s"&gt;sonar.sources=src&lt;/span&gt;
&lt;span class="s"&gt;sonar.tests=tests&lt;/span&gt;
&lt;span class="s"&gt;sonar.javascript.lcov.reportPaths=coverage/lcov.info&lt;/span&gt;
&lt;span class="s"&gt;sonar.exclusions=**/node_modules/**,**/dist/**&lt;/span&gt;
&lt;span class="s"&gt;sonar.coverage.exclusions=**/*.test.ts,**/*.spec.ts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Community Build is free (no branch analysis or PR decoration). SonarQube Cloud starts free for up to 50K LOC. Self-hosted Developer Edition starts at approximately $2,500/year. Enterprise Edition starts at approximately $20,000/year.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; SonarQube is the right choice for enterprise teams that need quality gate enforcement, compliance reporting, and long-term technical debt tracking. For smaller teams, the Community Build is a strong free option, though the lack of PR decoration limits its usefulness in pull request workflows. For setup help, see our &lt;a href="https://dev.to/blog/how-to-setup-sonarqube/"&gt;SonarQube setup guide&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  18. CodeClimate - Maintainability metrics and technical debt tracking
&lt;/h3&gt;

&lt;p&gt;CodeClimate Quality analyzes your JavaScript and TypeScript code for maintainability, providing letter grades (A through F) for every file based on complexity, duplication, and structural issues. It visualizes technical debt as estimated remediation time, making it easy for non-technical stakeholders to understand code health.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Maintainability ratings with letter grades for every file. Technical debt estimation in hours/days of remediation time. Code duplication detection across the entire project. Cognitive complexity scoring that identifies functions that are too hard to understand. Test coverage integration with trend tracking. GitHub and GitLab PR integration with inline annotations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free for open-source projects. Paid plans start at approximately $600/month for private repositories. Enterprise pricing is custom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; CodeClimate is best for teams that want maintainability metrics and technical debt visibility, particularly when communicating code health to engineering leadership. Its letter-grade system makes code quality tangible for stakeholders who do not read lint output. However, its rule depth for JavaScript is narrower than SonarQube, and it does not offer security scanning.&lt;/p&gt;




&lt;h3&gt;
  
  
  19. DeepSource - Autofix with the lowest false positive rate
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/deepsource/"&gt;DeepSource&lt;/a&gt; is a code quality platform that optimizes for precision over detection volume. It reports a sub-5% false positive rate - the lowest of any platform we have evaluated. For JavaScript and TypeScript, DeepSource detects anti-patterns, performance issues, potential bugs, and security vulnerabilities with AI-powered autofix that generates fix PRs automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Sub-5% false positive rate - when DeepSource flags an issue, developers trust it. AI Autofix generates pull requests with correct fixes for roughly 70% of detected issues. JavaScript and TypeScript analyzers cover over 150 rules including React, Node.js, and Express patterns. Performance issue detection including unnecessary re-renders, memory leaks, and inefficient data structures. Integration with GitHub, GitLab, and Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free for individual developers with unlimited repositories. Team plan at $12/user/month. Enterprise at custom pricing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; DeepSource is the best choice for teams that have been burned by noisy analysis tools. If your developers ignore code quality findings because too many are false positives, DeepSource's precision-first approach restores trust. The AI Autofix feature is particularly valuable for JavaScript projects where fixes like adding null checks or replacing deprecated API calls are repetitive. For a deeper look, see our &lt;a href="https://dev.to/blog/deepsource-review/"&gt;DeepSource review&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  20. Codacy - Automated reviews with 40+ tool integrations
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/codacy/"&gt;Codacy&lt;/a&gt; is an all-in-one code quality platform that bundles multiple analysis engines under a single dashboard. For JavaScript and TypeScript, it runs ESLint, Semgrep, PMD, and other engines simultaneously, aggregating findings into a unified view with code quality grades, coverage tracking, and security scanning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Aggregates findings from 40+ analysis engines including ESLint, Semgrep, and JSHint. Supports 49 programming languages - the widest coverage of any platform on this list. SAST security scanning, SCA dependency scanning, and secrets detection included. Coverage tracking with integration for Istanbul, Jest, and other JavaScript coverage reporters. AI Guardrails free IDE extension scans AI-generated code in VS Code, Cursor, and Windsurf in real time. Predictable pricing at $15/user/month with unlimited scans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free tier covers up to 5 repositories. Pro plan at $15/user/month. Business plan with self-hosted options at approximately $37.50/user/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Codacy is the best value option for teams of 10-50 developers who want code quality, security scanning, and coverage tracking without managing multiple tools. At $15/user/month, a 20-person team pays $300/month for comprehensive analysis - less than most competitors charge for code review alone. For integration guides, see our &lt;a href="https://dev.to/blog/codacy-github-integration/"&gt;Codacy GitHub integration&lt;/a&gt; and &lt;a href="https://dev.to/blog/how-to-setup-codacy/"&gt;Codacy setup guide&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI code reviewers
&lt;/h2&gt;

&lt;p&gt;AI code review tools use large language models to analyze pull requests and provide feedback that goes beyond what rule-based linters can detect. They catch logic errors, identify performance anti-patterns, flag security issues, and suggest improvements based on understanding the intent of code - not just its syntax.&lt;/p&gt;

&lt;h3&gt;
  
  
  21. CodeRabbit - AI PR review with zero-config setup
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/coderabbit/"&gt;CodeRabbit&lt;/a&gt; is the most widely installed AI code review tool on GitHub, with over 2 million repositories connected and 13 million pull requests reviewed. It uses LLMs to understand code semantics and provides structured PR reviews with walkthrough summaries, inline comments, and one-click fix suggestions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What makes it stand out for JavaScript.&lt;/strong&gt; CodeRabbit understands JavaScript and TypeScript idioms in a way that rule-based tools cannot. It catches issues like using &lt;code&gt;==&lt;/code&gt; where &lt;code&gt;===&lt;/code&gt; is needed in a context where type coercion would cause a subtle bug, missing error boundaries in React component trees, promises that are constructed but never awaited in an async flow, and API endpoints that accept user input without validation. The natural language instruction system lets you define review behavior in plain English:&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="c1"&gt;# .coderabbit.yaml&lt;/span&gt;
&lt;span class="na"&gt;reviews&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Flag&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;any&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;React&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;component&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;that&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;useEffect&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;without&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;dependency&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;array"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Warn&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;when&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;routes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;do&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;validate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;request&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;with&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Zod&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;or&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Joi"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;that&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;database&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;queries&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;use&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;parameterized&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;inputs"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ensure&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;responses&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;include&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;appropriate&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;HTTP&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;codes"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; PR walkthrough generation with file-by-file summaries explaining what changed and why. Inline code suggestions with one-click apply. Over 40 built-in linters complementing AI analysis with deterministic rules. Natural language configuration via &lt;code&gt;.coderabbit.yaml&lt;/code&gt;. Multi-platform support for GitHub, GitLab, Azure DevOps, and Bitbucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free tier with unlimited repositories and unlimited reviews. Pro plan at $24/user/month adds advanced features and priority support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; CodeRabbit is the default recommendation for any JavaScript team that wants AI-powered PR review. The free tier is generous enough for most small to mid-size teams. Pair it with a linter (ESLint or Biome) and a formatter (Prettier or Biome) for comprehensive coverage. For setup instructions, see our &lt;a href="https://dev.to/blog/how-to-setup-coderabbit/"&gt;CodeRabbit setup guide&lt;/a&gt; and &lt;a href="https://dev.to/blog/coderabbit-configuration/"&gt;configuration guide&lt;/a&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  22. CodeAnt AI - AI code health with auto-fix suggestions
&lt;/h3&gt;

&lt;p&gt;CodeAnt AI is a Y Combinator-backed platform that combines AI-powered PR reviews with SAST security scanning, secret detection, infrastructure-as-code security, and DORA engineering metrics. For JavaScript teams, it provides a single platform that replaces the need for separate review, security, and engineering metrics tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Line-by-line AI PR feedback with one-click auto-fix suggestions. SAST scanning covering OWASP Top 10 vulnerabilities in JavaScript and TypeScript. Secrets detection for accidentally committed API keys, tokens, and credentials. IaC scanning for Terraform and CloudFormation misconfigurations. DORA metrics for tracking deployment frequency, lead time, and change failure rate. Supports GitHub, GitLab, Bitbucket, and Azure DevOps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free Basic plan with AI code review. Premium plan at $24/user/month adds SAST, secrets, IaC, and DORA metrics. Enterprise pricing at $40/user/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; CodeAnt AI is the best choice for JavaScript teams that want a consolidated platform covering code review and security without stitching together multiple tools. At $24-40/user/month, it replaces what would otherwise require separate subscriptions for an AI review tool, a SAST scanner, and a secrets detector.&lt;/p&gt;




&lt;h3&gt;
  
  
  23. GitHub Copilot Code Review - Built into GitHub, agentic review
&lt;/h3&gt;

&lt;p&gt;GitHub Copilot Code Review is GitHub's native AI review feature, available to Copilot subscribers. It reviews pull requests directly within the GitHub UI, leaving comments, suggesting fixes, and identifying potential issues. In 2026, GitHub has expanded it with agentic capabilities - Copilot can now follow up on its own suggestions and verify fixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; Native GitHub integration - no third-party app installation required. Suggests code changes directly as GitHub suggested changes that can be committed with one click. Understands GitHub-specific context like issue references, linked PRs, and repository conventions. Agentic mode can perform multi-step review tasks. Available on all Copilot plans.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Included with GitHub Copilot Individual ($19/user/month), Pro+ ($39/user/month), Business ($39/user/month), and Enterprise ($39/user/month).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; If your team already uses GitHub Copilot, the code review feature is included at no additional cost. It provides useful first-pass review, though dedicated tools like CodeRabbit and CodeAnt AI offer deeper analysis with better cross-file context and more actionable suggestions. For a detailed comparison, see our &lt;a href="https://dev.to/blog/coderabbit-vs-github-copilot/"&gt;CodeRabbit vs GitHub Copilot&lt;/a&gt; analysis.&lt;/p&gt;




&lt;h3&gt;
  
  
  24. Sourcery - AI-powered refactoring suggestions
&lt;/h3&gt;

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

&lt;p&gt;&lt;a href="https://dev.to/tool/sourcery/"&gt;Sourcery&lt;/a&gt; focuses on code quality improvement through AI-powered refactoring suggestions. For JavaScript and TypeScript projects, it identifies code that works but could be written more clearly, more efficiently, or more idiomatically - and suggests specific refactorings.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key features.&lt;/strong&gt; AI-powered refactoring suggestions that go beyond linting - Sourcery identifies opportunities to simplify complex conditionals, extract helper functions, replace imperative loops with functional patterns, and improve variable naming. PR review integration with GitHub and GitLab. IDE extension for real-time suggestions in VS Code. Supports JavaScript, TypeScript, Python, and Go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pricing.&lt;/strong&gt; Free for open-source projects. Pro plan at $10/user/month.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use it.&lt;/strong&gt; Sourcery is the best choice for teams that care specifically about code readability and want AI-driven refactoring suggestions alongside standard linting. At $10/user/month, it is the most affordable AI review option. Its narrower language support (4 languages) means it is best for teams working primarily in JavaScript/TypeScript and Python. For a comparison with alternatives, see our &lt;a href="https://dev.to/blog/coderabbit-vs-sourcery/"&gt;CodeRabbit vs Sourcery&lt;/a&gt; analysis.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to choose the right tools
&lt;/h2&gt;

&lt;p&gt;With 24 tools across six categories, the decision can feel overwhelming. Here is a practical framework for choosing the right combination based on your project type and team size.&lt;/p&gt;

&lt;h3&gt;
  
  
  By project type
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Frontend-only (React, Vue, Svelte).&lt;/strong&gt; Your primary concerns are code quality, bundle size, and accessibility. Start with ESLint (or Biome) + Prettier + webpack-bundle-analyzer or source-map-explorer. Add CodeRabbit for AI review on teams of 3+.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Full-stack (Next.js, Nuxt, SvelteKit).&lt;/strong&gt; You need both frontend and backend coverage. Use ESLint with typescript-eslint for type-aware linting, Prettier for formatting, Semgrep or Snyk Code for security scanning on API routes, and CodeRabbit for comprehensive PR review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js backend (Express, Fastify, NestJS).&lt;/strong&gt; Security is your top priority. Use ESLint with typescript-eslint, Semgrep for SAST (especially injection and auth patterns), npm audit + Socket for dependency security, and SonarQube or Codacy for code quality tracking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;npm library or open-source package.&lt;/strong&gt; Consistency and contributor experience matter most. Use Biome (or ESLint + Prettier), bundlephobia to keep your package lean, and CodeRabbit (free for unlimited repos) for reviewing contributor PRs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recommended tool combinations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Starter stack (free, solo developers and small teams).&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linting&lt;/td&gt;
&lt;td&gt;ESLint or Biome&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formatting&lt;/td&gt;
&lt;td&gt;Prettier or Biome&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;npm audit + Semgrep OSS&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Review&lt;/td&gt;
&lt;td&gt;CodeRabbit (free tier)&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$0/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Intermediate stack (growing teams, 5-20 developers).&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linting&lt;/td&gt;
&lt;td&gt;ESLint + typescript-eslint&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formatting&lt;/td&gt;
&lt;td&gt;Prettier&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Semgrep (free for 10 contributors)&lt;/td&gt;
&lt;td&gt;Free-$35/contributor/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Quality&lt;/td&gt;
&lt;td&gt;Codacy or DeepSource&lt;/td&gt;
&lt;td&gt;$12-15/user/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Review&lt;/td&gt;
&lt;td&gt;CodeRabbit Pro&lt;/td&gt;
&lt;td&gt;$24/user/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundle Analysis&lt;/td&gt;
&lt;td&gt;source-map-explorer&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total (10 devs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$390/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Enterprise stack (50+ developers, compliance requirements).&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Linting&lt;/td&gt;
&lt;td&gt;ESLint + typescript-eslint&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Formatting&lt;/td&gt;
&lt;td&gt;Prettier&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Snyk Code + Socket&lt;/td&gt;
&lt;td&gt;$25/dev/mo + paid plan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code Quality&lt;/td&gt;
&lt;td&gt;SonarQube Enterprise&lt;/td&gt;
&lt;td&gt;~$20,000/yr&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Review&lt;/td&gt;
&lt;td&gt;CodeRabbit Enterprise&lt;/td&gt;
&lt;td&gt;$24/user/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Supply Chain&lt;/td&gt;
&lt;td&gt;npm audit + Socket&lt;/td&gt;
&lt;td&gt;Free + paid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Bundle Analysis&lt;/td&gt;
&lt;td&gt;webpack-bundle-analyzer&lt;/td&gt;
&lt;td&gt;Free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total (50 devs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~$3,900/month&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Setting up a complete JavaScript analysis pipeline
&lt;/h2&gt;

&lt;p&gt;The real power of code analysis comes from combining multiple tools into an automated pipeline that runs on every pull request. Here is a production-ready GitHub Actions workflow that combines ESLint, Prettier, Semgrep, and CodeRabbit.&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="c1"&gt;# .github/workflows/code-analysis.yml&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Analysis&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;develop&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&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;lint-and-format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Lint &amp;amp; Format Check&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;actions/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;22"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&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;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run ESLint&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;npx eslint . --max-warnings=0&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Prettier formatting&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;npx prettier --check .&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run TypeScript type check&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;npx tsc --noEmit&lt;/span&gt;

  &lt;span class="na"&gt;security-scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Security Scanning&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Semgrep&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;semgrep/semgrep-action@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;-&lt;/span&gt;
            &lt;span class="s"&gt;p/javascript&lt;/span&gt;
            &lt;span class="s"&gt;p/typescript&lt;/span&gt;
            &lt;span class="s"&gt;p/react&lt;/span&gt;
            &lt;span class="s"&gt;p/nodejs&lt;/span&gt;
            &lt;span class="s"&gt;p/owasp-top-ten&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;SEMGREP_APP_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.SEMGREP_APP_TOKEN }}&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/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;22"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&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;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run npm audit&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;npm audit --audit-level=high&lt;/span&gt;

  &lt;span class="na"&gt;code-quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Quality&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;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name == 'pull_request'&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="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&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/setup-node@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;node-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;22"&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;npm"&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;npm ci&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run tests with coverage&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;npx vitest run --coverage&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check bundle size&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;npm run build&lt;/span&gt;
          &lt;span class="s"&gt;npx source-map-explorer dist/**/*.js --json &amp;gt; bundle-report.json&lt;/span&gt;
          &lt;span class="s"&gt;echo "Bundle analysis complete. Check artifacts for details."&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload coverage&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/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;coverage-report&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;coverage/&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Upload bundle report&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/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle-report&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bundle-report.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How the pipeline works.&lt;/strong&gt; Three jobs run in parallel for maximum speed. The lint-and-format job catches style violations and type errors in under 30 seconds. The security-scan job runs Semgrep against JavaScript, TypeScript, React, Node.js, and OWASP Top 10 rulesets, then checks dependencies with npm audit. The code-quality job runs tests with coverage and generates a bundle size report. CodeRabbit runs separately via its GitHub App - once installed, it automatically reviews every PR with no workflow configuration needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding CodeRabbit.&lt;/strong&gt; CodeRabbit does not require a GitHub Actions workflow. Install the CodeRabbit GitHub App, grant it repository access, and it begins reviewing PRs automatically. To customize its behavior, add a &lt;code&gt;.coderabbit.yaml&lt;/code&gt; file to your repository root:&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="c1"&gt;# .coderabbit.yaml&lt;/span&gt;
&lt;span class="na"&gt;language&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en&lt;/span&gt;
&lt;span class="na"&gt;reviews&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;profile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;chill&lt;/span&gt;
  &lt;span class="na"&gt;high_level_summary&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;poem&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
  &lt;span class="na"&gt;path_instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/api/**"&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;API&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;routes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;for&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;input&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;validation&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;proper&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;handling"&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/components/**"&lt;/span&gt;
      &lt;span class="na"&gt;instructions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Verify&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;React&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;components&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;handle&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;loading&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;error&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;states"&lt;/span&gt;
  &lt;span class="na"&gt;auto_review&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;drafts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This configuration tells CodeRabbit to use a balanced review tone, generate high-level summaries for each PR, apply stricter review criteria for API routes and React components, and skip draft PRs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key takeaways
&lt;/h2&gt;

&lt;p&gt;JavaScript code analysis in 2026 is not about picking a single tool - it is about building a layered pipeline where each tool handles what it does best.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linters catch syntax and pattern issues.&lt;/strong&gt; ESLint remains the standard for its plugin ecosystem, but Biome is the faster modern alternative. Every JavaScript project needs one or the other.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Formatters eliminate style debates.&lt;/strong&gt; Prettier is the established choice. Biome's built-in formatter is a strong alternative if you are already using Biome for linting. Either way, formatting should be automated, not discussed in code review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bundler analyzers keep your builds lean.&lt;/strong&gt; Check webpack-bundle-analyzer or source-map-explorer periodically. Use bundlephobia before adding any new dependency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security scanners protect your code and dependencies.&lt;/strong&gt; Semgrep and Snyk Code catch vulnerabilities in your source code. npm audit and Socket protect your dependency tree. Use at least one tool from each category.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code quality platforms track health over time.&lt;/strong&gt; SonarQube, Codacy, and DeepSource provide dashboards, quality gates, and trend analysis that point-in-time tools cannot offer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI reviewers catch what rules miss.&lt;/strong&gt; CodeRabbit, CodeAnt AI, and GitHub Copilot Code Review understand code intent and catch logic errors, performance issues, and architectural problems that no amount of lint rules can detect.&lt;/p&gt;

&lt;p&gt;The best JavaScript analysis setup is one your team actually uses. Start with the free starter stack - ESLint, Prettier, npm audit, and CodeRabbit's free tier. That combination costs nothing and catches the majority of issues. Add specialized tools as your project grows, your team scales, or your compliance requirements demand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/unit-testing-mocha-chai/"&gt;Unit Testing with Mocha and Chai: The Complete JavaScript Guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/react-autocomplete-search-hooks/"&gt;Build an Autocomplete Search Bar with React and TypeScript&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/best-ai-code-review-tools/"&gt;Best AI Code Review Tools in 2026 - Expert Picks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/best-sast-tools-2026/"&gt;I Reviewed 32 SAST Tools - Here Are the Ones Actually Worth Using (2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/best-code-review-tools-javascript/"&gt;Best Code Review Tools for JavaScript and TypeScript in 2026&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/best-code-quality-tools/"&gt;13 Best Code Quality Tools in 2026 - Platforms, Linters, and Metrics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/how-to-setup-coderabbit/"&gt;How to Set Up CodeRabbit for AI-Powered Code Review&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the best JavaScript linter in 2026?
&lt;/h3&gt;

&lt;p&gt;ESLint remains the dominant JavaScript linter with the largest ecosystem of plugins and rules. However, Biome (the Rome successor) is gaining traction as a faster alternative that combines linting and formatting. For new projects, Biome offers the best performance; for existing projects with complex ESLint configs, staying with ESLint v9's flat config is recommended.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the fastest JavaScript linter?
&lt;/h3&gt;

&lt;p&gt;Biome is the fastest JavaScript linter, running 20-35x faster than ESLint on most codebases. It's written in Rust and handles both linting and formatting in a single pass. oxlint is another Rust-based alternative that's even faster for pure linting but has fewer rules than Biome.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do I need both a linter and a formatter for JavaScript?
&lt;/h3&gt;

&lt;p&gt;Yes, but modern tools combine both. Biome handles linting and formatting in one tool. If using ESLint, pair it with Prettier for formatting. The ESLint team recommends against using ESLint for formatting rules (they deprecated stylistic rules) and suggests using a dedicated formatter.&lt;/p&gt;

&lt;h3&gt;
  
  
  What tools detect security vulnerabilities in JavaScript code?
&lt;/h3&gt;

&lt;p&gt;Semgrep (free, rule-based SAST), Snyk Code (AI-powered, cross-file analysis), SonarQube (broad security rules), and npm audit (dependency vulnerabilities). For React-specific security, eslint-plugin-security and Semgrep's JavaScript rulesets cover XSS, injection, and prototype pollution patterns.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are AI code review tools worth it for JavaScript projects?
&lt;/h3&gt;

&lt;p&gt;Yes. AI tools like CodeRabbit catch logic errors, performance anti-patterns, and security issues that rule-based linters miss. They're particularly valuable for reviewing business logic, API usage patterns, and architectural decisions. Most teams use them alongside traditional linters for comprehensive coverage.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://aicodereview.cc/blog/javascript-code-analysis-tools/" rel="noopener noreferrer"&gt;aicodereview.cc&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>codereview</category>
      <category>ai</category>
      <category>programming</category>
      <category>tools</category>
    </item>
    <item>
      <title>Stop Burning Money on AI: Cost Tracking &amp; Rate Limiting for Local LLMs</title>
      <dc:creator>Programming Central</dc:creator>
      <pubDate>Fri, 03 Apr 2026 20:00:00 +0000</pubDate>
      <link>https://scale.forem.com/programmingcentral/stop-burning-money-on-ai-cost-tracking-rate-limiting-for-local-llms-3dni</link>
      <guid>https://scale.forem.com/programmingcentral/stop-burning-money-on-ai-cost-tracking-rate-limiting-for-local-llms-3dni</guid>
      <description>&lt;p&gt;Running Large Language Models (LLMs) locally offers incredible privacy and control, but it’s easy to spin up costs you didn’t anticipate.  Just like a cloud API bills per token, your local LLM consumes valuable resources – CPU, GPU, memory, and even electricity.  Without careful management, you risk system instability, poor user experience, and ultimately, wasted hardware. This post dives into the operational economics of local AI, showing you how to track costs and implement rate limiting to keep your LLM applications running smoothly and efficiently.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Economics of Local AI: From Compute to Cash
&lt;/h2&gt;

&lt;p&gt;We’ve all been there: a functional prototype that works beautifully… until multiple users hit it simultaneously.  Integrating LLMs into your Node.js applications (using tools like Ollama and Transformers.js) is just the first step.  To move to production, you need to treat inference as a finite resource with tangible costs. &lt;/p&gt;

&lt;p&gt;Think of it like a database. A query consumes CPU cycles and I/O. An LLM request consumes computational power, memory bandwidth, and &lt;em&gt;time&lt;/em&gt;.  In the cloud, this translates directly to monetary cost per token. Locally, the cost manifests as hardware wear, electricity, and, crucially, the opportunity cost of blocking the system for other users.  A runaway LLM process can easily bring a server to its knees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Costs: Token Usage, Latency &amp;amp; Hardware
&lt;/h2&gt;

&lt;p&gt;Effective cost tracking requires understanding the key metrics. It’s more granular than traditional web application monitoring.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Token Throughput: The Core Metric
&lt;/h3&gt;

&lt;p&gt;The most fundamental metric is &lt;strong&gt;token throughput&lt;/strong&gt; (tokens per second - TPS).  Break it down:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Input Tokens:&lt;/strong&gt; Tokens in the user’s prompt &lt;em&gt;and&lt;/em&gt; any retrieved context (from RAG – Retrieval Augmented Generation).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Output Tokens:&lt;/strong&gt; Tokens generated by the model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; LLM execution time is proportional to sequence length. Generating 100 tokens takes roughly twice as long as 50. This non-deterministic latency demands a different mental model than standard HTTP request-response cycles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt; Imagine an LLM as a novelist. Input tokens are research notes, output tokens are written pages, and latency is the time to write. You can’t predict the book’s completion time until the final sentence. Similarly, you can’t know the exact inference cost until generation finishes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Hardware Overhead: VRAM &amp;amp; Compute
&lt;/h3&gt;

&lt;p&gt;In a local deployment, “cost” is physical. The primary constraints are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;VRAM (Video RAM):&lt;/strong&gt;  Memory for model weights and the "KV Cache" (Key-Value Cache).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Compute Units:&lt;/strong&gt; GPU core or CPU vector instruction utilization.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The KV Cache Bottleneck:&lt;/strong&gt;  LLMs remember context by storing intermediate calculations (Keys and Values) in memory. This cache grows linearly with input &lt;em&gt;and&lt;/em&gt; output tokens.  An 8GB model on a 12GB GPU has 4GB headroom. But a 50,000-token document as context could fill that 4GB, causing an Out-Of-Memory (OOM) error – a costly crash.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt; Think of a restaurant kitchen. Model weights are permanent appliances. The KV Cache is counter space for preparing a dish. A simple dish needs little space; a complex banquet needs &lt;em&gt;all&lt;/em&gt; of it. Rate limiting prevents accepting orders that overwhelm the kitchen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rate Limiting: The Token Bucket Algorithm
&lt;/h2&gt;

&lt;p&gt;Rate limiting protects your hardware from overload.  Unlike cloud APIs that throttle based on credits, local servers throttle based on capacity.  The &lt;strong&gt;Token Bucket&lt;/strong&gt; algorithm is superior to simple "requests per minute" counters because it handles traffic bursts while enforcing a steady-state average.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;The Bucket:&lt;/strong&gt; A container with a maximum capacity (burst size).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Tokens:&lt;/strong&gt; Represent "permission to process."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Refill Rate:&lt;/strong&gt; Tokens are added at a fixed rate (e.g., 10 tokens/second).&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Request:&lt;/strong&gt; Consumes tokens. If the bucket is empty, the request is rejected (or queued).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Optimizing Performance: Batching &amp;amp; Context Management
&lt;/h2&gt;

&lt;p&gt;Maximizing value requires optimizing hardware usage.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Request Batching
&lt;/h3&gt;

&lt;p&gt;Modern inference engines (like Ollama) support dynamic batching – grouping multiple requests into a single inference call. GPUs process matrix multiplications in parallel. Sending four requests simultaneously fills the GPU cores more efficiently, increasing aggregate throughput, even if per-request latency increases slightly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Analogy:&lt;/strong&gt; A city bus is more efficient than individual taxis for transporting many passengers.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Context Window Management
&lt;/h3&gt;

&lt;p&gt;The "Context Window" is the maximum text the model can consider (e.g., 4096 or 8192 tokens). In RAG, this is critical.  Blindly pasting retrieved text can exceed the window or “drown out” the user’s question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Optimization Strategies:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Re-ranking:&lt;/strong&gt; Use a smaller model to score the relevance of retrieved chunks and keep only the top-N.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Summarization:&lt;/strong&gt; Summarize long context before injection.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Code Example: TypeScript Token Bucket Rate Limiter
&lt;/h2&gt;

&lt;p&gt;This example implements a Token Bucket algorithm with cost tracking in TypeScript, suitable for a Node.js backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/chat.ts&lt;/span&gt;
&lt;span class="c1"&gt;// Next.js API Route Handler&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;NextApiRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextApiResponse&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;next&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterConfig&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;refillRate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;costPerRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;lastRefill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;CostMetrics&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;promptTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;completionTokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;latencyMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;estimatedComputeCost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ChatRequestPayload&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;CONFIG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;refillRate&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="na"&gt;costPerRequest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userStates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterState&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;function&lt;/span&gt; &lt;span class="nf"&gt;refillBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterState&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;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;timePassed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastRefill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&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;tokensToAdd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;timePassed&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;refillRate&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;newTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;tokensToAdd&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastRefill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;now&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;tryConsume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;RateLimiterState&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;RateLimiterState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;boolean&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;refilledState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;refillBucket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refilledState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;costPerRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;refilledState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;refilledState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;costPerRequest&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;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;refilledState&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleRequest&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;NextApiRequest&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;NextApiResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&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;body&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ChatRequestPayload&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;userId&lt;/span&gt; &lt;span class="o"&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;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;anonymous&lt;/span&gt;&lt;span class="dl"&gt;'&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;currentState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userStates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;capacity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;lastRefill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;allowed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;tryConsume&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;userStates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;allowed&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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;429&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Too many requests&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;// Simulate LLM inference (replace with Ollama/Transformers.js call)&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&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;promptTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Rough estimate&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;completionTokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Example&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latencyMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;latencyMs&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;costMetrics&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CostMetrics&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;promptTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;completionTokens&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;latencyMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;estimatedComputeCost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Example cost&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;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Processed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;query&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;costMetrics&lt;/span&gt; &lt;span class="p"&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="nx"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion:  Sustainable Local AI
&lt;/h2&gt;

&lt;p&gt;Cost tracking and rate limiting aren’t just about preventing crashes; they’re about building &lt;em&gt;sustainable&lt;/em&gt; local AI applications. By understanding the economics of inference and implementing these techniques, you can maximize the value of your hardware, deliver a consistent user experience, and avoid unexpected costs.  Remember to adapt the configuration and metrics to your specific hardware and application needs.  Don't just run your LLM – &lt;em&gt;manage&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;The concepts and code demonstrated here are drawn directly from the comprehensive roadmap laid out in the book &lt;strong&gt;The Edge of AI. Local LLMs (Ollama), Transformers.js, WebGPU, and Performance Optimization&lt;/strong&gt; &lt;a href="https://www.amazon.com/gp/product/B0GK52M4R2" rel="noopener noreferrer"&gt;Amazon Link&lt;/a&gt; of the &lt;a href="https://www.amazon.com/dp/B0G59X6X7W" rel="noopener noreferrer"&gt;AI with JavaScript &amp;amp; TypeScript Series&lt;/a&gt;. &lt;br&gt;
The ebook is also on Leanpub.com: &lt;a href="https://leanpub.com/EdgeOfAIJavaScriptTypeScript" rel="noopener noreferrer"&gt;https://leanpub.com/EdgeOfAIJavaScriptTypeScript&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://programmingcentral.vercel.app/books/typescript/" rel="noopener noreferrer"&gt;Free Access now to the TypeScript &amp;amp; AI Series&lt;/a&gt; on Programming Central, it includes &lt;strong&gt;8 Volumes, 160 Chapters and hundreds of quizzes for every chapter&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Building a GDPR-Compliant Multi-Tenant CRM with Laravel</title>
      <dc:creator>WISSEN BERATUNG</dc:creator>
      <pubDate>Fri, 03 Apr 2026 19:59:44 +0000</pubDate>
      <link>https://scale.forem.com/wb_crm/building-a-gdpr-compliant-multi-tenant-crm-with-laravel-3n1o</link>
      <guid>https://scale.forem.com/wb_crm/building-a-gdpr-compliant-multi-tenant-crm-with-laravel-3n1o</guid>
      <description>&lt;p&gt;Building a CRM that handles personal data (names, emails, phone numbers, addresses) in the EU means you can't treat GDPR as an afterthought. Here's how we implemented it in WB-CRM, our multi-tenant CRM built with Laravel 12.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Database-per-Tenant Architecture
&lt;/h4&gt;

&lt;p&gt;We use &lt;a href="https://tenancyforlaravel.com/" rel="noopener noreferrer"&gt;stancl/tenancy&lt;/a&gt; v3 with database-per-tenant isolation. Each tenant gets their own MySQL database (&lt;code&gt;tenant_acme&lt;/code&gt;, &lt;code&gt;tenant_demo&lt;/code&gt;, etc.).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Central models explicitly set their connection&lt;/span&gt;
&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$connection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'central'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Tenant models rely on the bootstrapper — no $connection property&lt;/span&gt;
&lt;span class="c1"&gt;// stancl/tenancy switches the default connection automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why not shared database with row-level security?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In a shared database, one missing &lt;code&gt;WHERE tenant_id = ?&lt;/code&gt; clause leaks data across companies. With DB-per-tenant, it's architecturally impossible.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Field-Level Encryption for PII
&lt;/h4&gt;

&lt;p&gt;Laravel's &lt;code&gt;encrypted&lt;/code&gt; cast encrypts individual database fields with AES-256:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;casts&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="s1"&gt;'name'&lt;/span&gt;       &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'email'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'phone'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'address'&lt;/span&gt;    &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'ip_address'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'encrypted'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The tradeoff:&lt;/strong&gt; You can't query encrypted fields with &lt;code&gt;WHERE email = ?&lt;/code&gt;. We solve this with companion hash columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// email_hash stores a HMAC-SHA256 hash for lookups&lt;/span&gt;
&lt;span class="nv"&gt;$contact&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Contact&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email_hash'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;hash_hmac&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'sha256'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.key'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. GDPR Data Subject Rights in Code
&lt;/h4&gt;

&lt;p&gt;Articles 15-21 are not just checkboxes — they need actual endpoints:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Right&lt;/th&gt;
&lt;th&gt;Article&lt;/th&gt;
&lt;th&gt;Implementation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Access&lt;/td&gt;
&lt;td&gt;Art. 15&lt;/td&gt;
&lt;td&gt;JSON/CSV export endpoint with re-authentication&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rectification&lt;/td&gt;
&lt;td&gt;Art. 16&lt;/td&gt;
&lt;td&gt;Profile edit functionality&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Erasure&lt;/td&gt;
&lt;td&gt;Art. 17&lt;/td&gt;
&lt;td&gt;Account deletion + cascading DB cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restriction&lt;/td&gt;
&lt;td&gt;Art. 18&lt;/td&gt;
&lt;td&gt;Account freeze (disable without delete)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Portability&lt;/td&gt;
&lt;td&gt;Art. 20&lt;/td&gt;
&lt;td&gt;Machine-readable export (JSON)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Objection&lt;/td&gt;
&lt;td&gt;Art. 21&lt;/td&gt;
&lt;td&gt;Marketing opt-out&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every GDPR operation writes an audit log entry with: who, when, what, which tenant, old values, new values.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Audit Trail
&lt;/h4&gt;

&lt;p&gt;Every state-changing operation produces an audit log entry. This is mandatory for GDPR Art. 30 (records of processing activities):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;TenantAuditLog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'uuid'&lt;/span&gt;           &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Str&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'auditable_type'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;get_class&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'auditable_id'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'event'&lt;/span&gt;          &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'gdpr_data_export'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'old_values'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'new_values'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'format'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'fields'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$exportedFields&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s1"&gt;'user_type'&lt;/span&gt;      &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;TenantUser&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'user_id'&lt;/span&gt;        &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$tenantUser&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s1"&gt;'ip_address'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;ip&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="s1"&gt;'user_agent'&lt;/span&gt;     &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;userAgent&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;
  
  
  5. What I Wish I Knew Earlier
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Herd/CLI bcrypt incompatibility&lt;/strong&gt;: CLI PHP and Herd PHP on macOS produce incompatible bcrypt hashes. Always set passwords from the web context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session connection must be &lt;code&gt;central&lt;/code&gt;&lt;/strong&gt;: With database-per-tenant, sessions stored in a tenant DB are lost when switching tenants.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ENUM columns are evil in migrations&lt;/strong&gt;: Adding a value requires recreating the column in MySQL. Use string columns with validation instead.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;getCustomColumns()&lt;/code&gt; in stancl/tenancy&lt;/strong&gt;: If you add a column to the tenants table but forget to register it in &lt;code&gt;getCustomColumns()&lt;/code&gt;, it silently lands in the JSON &lt;code&gt;data&lt;/code&gt; column. Hours of debugging.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;We built &lt;a href="https://wb-crm.net" rel="noopener noreferrer"&gt;WB-CRM&lt;/a&gt; with these principles. Free ONE plan available (500 contacts, 1 user). Built and hosted in Germany.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>saas</category>
      <category>php</category>
      <category>laravel</category>
    </item>
    <item>
      <title>MiniStack vs Floci vs LocalStack: Honest Performance Benchmark (April 3rd 2026)</title>
      <dc:creator>Nahuel Nucera</dc:creator>
      <pubDate>Fri, 03 Apr 2026 19:57:59 +0000</pubDate>
      <link>https://scale.forem.com/nahuel990/ministack-vs-floci-vs-localstack-honest-performance-benchmark-april-3rd-2026-479p</link>
      <guid>https://scale.forem.com/nahuel990/ministack-vs-floci-vs-localstack-honest-performance-benchmark-april-3rd-2026-479p</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;We ran 30 API operations, throughput tests, and service coverage checks against real Docker containers. No cherry-picking. No marketing. Just numbers.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;LocalStack Free&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Services supported&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20&lt;/td&gt;
&lt;td&gt;~15 (rest paywalled)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Image size&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;211 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;276 MB&lt;/td&gt;
&lt;td&gt;~1 GB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory after load&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;39 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;56 MB&lt;/td&gt;
&lt;td&gt;~500 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup time&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;lt;2s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~3s&lt;/td&gt;
&lt;td&gt;~15-30s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Median API latency&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.2 ms&lt;/td&gt;
&lt;td&gt;varies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;License&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;MIT&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MIT&lt;/td&gt;
&lt;td&gt;BSL (restricted)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Methodology
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Fresh &lt;code&gt;docker system prune -af --volumes&lt;/code&gt; before each run&lt;/li&gt;
&lt;li&gt;Both images pulled from Docker Hub (&lt;code&gt;nahuelnucera/ministack:latest&lt;/code&gt;, &lt;code&gt;hectorvent/floci:latest&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Each operation run &lt;strong&gt;5 times&lt;/strong&gt;, median taken&lt;/li&gt;
&lt;li&gt;All tests use boto3 with &lt;code&gt;endpoint_url=http://localhost:{port}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Machine: Apple Silicon M4, 24 GB RAM, Docker Desktop&lt;/li&gt;
&lt;li&gt;No warm-up runs — cold container, first request measured&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Image Size
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nahuelnucera/ministack:latest    211 MB
hectorvent/floci:latest          276 MB
localstack/localstack:latest    ~1.0 GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MiniStack is 24% smaller than Floci and 5x smaller than LocalStack. MiniStack uses Alpine + Python + Node.js. Floci uses a JVM-based stack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Startup Time
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;First Response&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MiniStack&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1 ms&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Floci&lt;/td&gt;
&lt;td&gt;15 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LocalStack&lt;/td&gt;
&lt;td&gt;15-30 seconds&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack starts instantly. No JVM warm-up, no class loading. The ASGI server is ready before the health check even fires.&lt;/p&gt;




&lt;h2&gt;
  
  
  API Latency (median of 5 runs, single operation)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  S3
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateBucket&lt;/td&gt;
&lt;td&gt;5.9 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-5%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutObject (1 KB)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.4 ms&lt;/td&gt;
&lt;td&gt;+2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutObject (100 KB)&lt;/td&gt;
&lt;td&gt;10.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;7.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-31%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetObject&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.4 ms&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ListObjectsV2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;6.2 ms&lt;/td&gt;
&lt;td&gt;+13%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;S3 is competitive. Floci edges ahead on small reads. MiniStack is significantly faster on larger writes (100 KB: 31% faster).&lt;/p&gt;

&lt;h3&gt;
  
  
  SQS
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateQueue&lt;/td&gt;
&lt;td&gt;4.5 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-2%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SendMessage&lt;/td&gt;
&lt;td&gt;9.8 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ReceiveMessage&lt;/td&gt;
&lt;td&gt;7.8 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-17%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack is consistently faster on SQS operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  DynamoDB
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CreateTable&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.4 ms&lt;/td&gt;
&lt;td&gt;-8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PutItem&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.2 ms&lt;/td&gt;
&lt;td&gt;+14%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GetItem&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.4 ms&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.0 ms&lt;/td&gt;
&lt;td&gt;+16%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scan&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.2 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;4.6 ms&lt;/td&gt;
&lt;td&gt;+10%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Floci wins on DynamoDB read/write operations. This is likely due to Java's optimized JSON parsing for the DynamoDB wire format.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Services
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Difference&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SNS CreateTopic&lt;/td&gt;
&lt;td&gt;3.9 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS Publish&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;8.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;8.8 ms&lt;/td&gt;
&lt;td&gt;+4%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM CreateRole&lt;/td&gt;
&lt;td&gt;5.0 ms&lt;/td&gt;
&lt;td&gt;5.9 ms&lt;/td&gt;
&lt;td&gt;+18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STS GetCallerIdentity&lt;/td&gt;
&lt;td&gt;5.1 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.5 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-12%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM PutParameter&lt;/td&gt;
&lt;td&gt;6.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-29%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM GetParameter&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.2 ms&lt;/td&gt;
&lt;td&gt;+11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretsManager Create&lt;/td&gt;
&lt;td&gt;4.8 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretsManager Get&lt;/td&gt;
&lt;td&gt;4.7 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-6%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge PutRule&lt;/td&gt;
&lt;td&gt;5.3 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.7 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-11%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge PutEvents&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.8 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5.5 ms&lt;/td&gt;
&lt;td&gt;+15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinesis CreateStream&lt;/td&gt;
&lt;td&gt;5.6 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;5.1 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-9%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CW PutMetricData&lt;/td&gt;
&lt;td&gt;4.9 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.4 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logs CreateLogGroup&lt;/td&gt;
&lt;td&gt;6.5 ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.6 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;-29%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Route53 CreateHostedZone&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;ERR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4.3 ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Floci doesn't support Route53&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack is faster on SSM, SecretsManager, CloudWatch, and Logs. Floci is faster on IAM and EventBridge PutEvents. Route53 only works on MiniStack.&lt;/p&gt;




&lt;h2&gt;
  
  
  Throughput
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SQS SendMessage x500&lt;/td&gt;
&lt;td&gt;221 ops/s&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;233 ops/s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;On sustained SQS throughput, MiniStack is 5% faster. Earlier cold-start benchmarks showed Floci ahead, but with warm containers the gap disappears.&lt;/p&gt;




&lt;h2&gt;
  
  
  Memory Usage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;State&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;At idle&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;26 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;38 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;After 500+ operations&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;56 MB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;39 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Interesting: Floci uses less memory at idle (JVM lazy class loading) but grows to 56 MB after load. MiniStack starts at 38 MB and barely grows. Over time, MiniStack's memory profile is more predictable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Service Coverage
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;S3&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DynamoDB&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IAM&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;STS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SecretsManager&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Logs&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSM&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;EventBridge&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kinesis&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudWatch Metrics&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Functions&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cognito&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudFormation&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ACM&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;KMS&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ElastiCache&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Glue&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Athena&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Firehose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Route53&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EC2/VPC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EMR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ELBv2/ALB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;WAF v2&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;YES&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Total&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;31&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;MiniStack supports 55% more services. The gap is particularly significant for infrastructure-heavy workloads (ECS, RDS with real Docker, EC2/VPC, Route53, ALB).&lt;/p&gt;




&lt;h2&gt;
  
  
  Feature Comparison
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;MiniStack&lt;/th&gt;
&lt;th&gt;Floci&lt;/th&gt;
&lt;th&gt;LocalStack Free&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Python execution&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda Node.js execution&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lambda warm workers&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RDS real Postgres/MySQL&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ECS real Docker containers&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ElastiCache real Redis&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Athena real SQL (DuckDB)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO (Pro)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CloudFormation&lt;/td&gt;
&lt;td&gt;YES (12 types)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Step Functions TestState API&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SFN Mock Config (SFN Local compat)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State persistence&lt;/td&gt;
&lt;td&gt;YES (20 services)&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;S3 disk persistence&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Detached mode (&lt;code&gt;-d&lt;/code&gt; / &lt;code&gt;--stop&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Terraform v6 compatible&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS SDK v2 chunked encoding&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testcontainers examples&lt;/td&gt;
&lt;td&gt;Java, Go, Python&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;Java&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;docker run&lt;/code&gt; one-liner&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PyPI installable&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;td&gt;NO&lt;/td&gt;
&lt;td&gt;YES&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  What Floci Does Better
&lt;/h2&gt;

&lt;p&gt;Let's be honest about where Floci wins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB read latency&lt;/strong&gt; — 15-16% faster on GetItem/Query/Scan. Java's JSON processing is well-optimized for DynamoDB's wire format.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idle memory&lt;/strong&gt; — 26 MB vs 38 MB at cold start. JVM defers class loading.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  What MiniStack Does Better
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;11 more services&lt;/strong&gt; — ECS, ElastiCache, Glue, Athena, Route53, EC2, EMR, ALB, WAF, Firehose, ECR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real infrastructure&lt;/strong&gt; — RDS spins up actual Postgres/MySQL. ECS runs real containers. Athena runs real SQL via DuckDB.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lambda Node.js&lt;/strong&gt; — warm worker pool for both Python and Node.js.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State persistence&lt;/strong&gt; — 20 services survive restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Faster on most operations&lt;/strong&gt; — SSM, SecretsManager, SQS, CloudWatch, Logs are 15-30% faster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Terraform v6 ready&lt;/strong&gt; — EC2 stubs, S3 Control routing, DynamoDB WarmThroughput.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smaller image&lt;/strong&gt; — 211 MB vs 276 MB (24% smaller).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  When to Use What
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use MiniStack if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need ECS, Route53, EC2, Glue, Athena, ALB, or any of the 11 extra services&lt;/li&gt;
&lt;li&gt;You're migrating from LocalStack and need maximum service coverage&lt;/li&gt;
&lt;li&gt;You want state persistence across container restarts&lt;/li&gt;
&lt;li&gt;You use Terraform v6&lt;/li&gt;
&lt;li&gt;You want Lambda Node.js support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Floci if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You only need the core 20 services&lt;/li&gt;
&lt;li&gt;DynamoDB read performance is critical for your test suite&lt;/li&gt;
&lt;li&gt;You want the smallest possible idle memory footprint&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use LocalStack Pro if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need IAM policy enforcement&lt;/li&gt;
&lt;li&gt;You need Lambda container image support&lt;/li&gt;
&lt;li&gt;Budget isn't a constraint&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Benchmark Reproducibility
&lt;/h2&gt;

&lt;p&gt;All benchmarks can be reproduced with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker system prune &lt;span class="nt"&gt;-af&lt;/span&gt; &lt;span class="nt"&gt;--volumes&lt;/span&gt;
docker pull nahuelnucera/ministack:latest
docker pull hectorvent/floci:latest

docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; ms &lt;span class="nt"&gt;-p&lt;/span&gt; 4568:4566 nahuelnucera/ministack:latest
docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;--name&lt;/span&gt; fl &lt;span class="nt"&gt;-p&lt;/span&gt; 4567:4566 hectorvent/floci:latest

&lt;span class="c"&gt;# Run your own boto3 tests against both ports&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Versions Tested
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;MiniStack: v1.1.27 (April 2026)&lt;/li&gt;
&lt;li&gt;Floci: latest (April 2026)&lt;/li&gt;
&lt;li&gt;LocalStack: comparison based on published documentation (not benchmarked directly)&lt;/li&gt;
&lt;li&gt;boto3: 1.34+&lt;/li&gt;
&lt;li&gt;Docker Desktop: latest&lt;/li&gt;
&lt;li&gt;Hardware: Apple M4, 24 GB RAM&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;This benchmark was created by the MiniStack team. We tried to be as fair as possible — if you find any methodology issues, please open an issue on &lt;a href="https://github.com/Nahuel990/ministack" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>aws</category>
      <category>testing</category>
    </item>
    <item>
      <title>I analyzed 187 Claude Code sessions. $6,744 worth of tokens. Here's where they actually went.</title>
      <dc:creator>SingYee</dc:creator>
      <pubDate>Fri, 03 Apr 2026 19:57:11 +0000</pubDate>
      <link>https://scale.forem.com/singggggyee/i-analyzed-187-claude-code-sessions-6744-worth-of-tokens-heres-where-they-actually-went-2ae5</link>
      <guid>https://scale.forem.com/singggggyee/i-analyzed-187-claude-code-sessions-6744-worth-of-tokens-heres-where-they-actually-went-2ae5</guid>
      <description>&lt;p&gt;I've been using Claude Code heavily for the past month. Building trading bots, automation tools, side projects.&lt;/p&gt;

&lt;p&gt;I knew I was burning through tokens but never looked at the numbers.&lt;/p&gt;

&lt;p&gt;So I built a small CLI to parse my local session data. The result: &lt;strong&gt;187 sessions. 3.3 billion tokens. $6,744 equivalent API cost.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknklfhwdiloxf4j0xy03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fknklfhwdiloxf4j0xy03.png" alt=" " width="800" height="599"&gt;&lt;/a&gt;I'm on Max, so this is equivalent API cost, not what I actually paid. But the token patterns are what matter here.&lt;/p&gt;

&lt;h2&gt;
  
  
  97% of my tokens were something I couldn't control
&lt;/h2&gt;

&lt;p&gt;That was the first surprise. 97% were cache reads. Every turn, Claude re-reads the entire conversation context. Think of it like re-reading an entire book every time you turn a page.&lt;/p&gt;

&lt;p&gt;The good news: cache reads are cheap ($1.5/M tokens) and completely normal. The bad news: it means the part you can actually control is tiny.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only 2.8% of my tokens were controllable.&lt;/strong&gt; Of that, 92.5% was cache creation (CLAUDE.md, MCP tools, system prompt loading), 6.6% was Claude's actual output, 0.9% was my input.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I wouldn't have caught from /cost
&lt;/h2&gt;

&lt;p&gt;This was the most useful part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;86 sessions&lt;/strong&gt; over 30 turns without /compact, each one letting context balloon to 2-3x what it needed to be&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;840 subagent calls&lt;/strong&gt;, every single one duplicating the full conversation context just to do a search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;35 anomaly sessions&lt;/strong&gt; burning tokens at 2-3x my normal rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bash was 40% of all tool calls&lt;/strong&gt;, pumping long command outputs back into context every time&lt;/li&gt;
&lt;li&gt;Peak hours (Mon-Fri 5-11am PT) used &lt;strong&gt;1.3x more tokens&lt;/strong&gt; on average than off-peak&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I actually changed
&lt;/h2&gt;

&lt;p&gt;After seeing the data, three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I use /compact after ~20 turns now instead of letting sessions run endlessly&lt;/li&gt;
&lt;li&gt;I stopped defaulting to Agent for codebase searches and use Grep/Glob directly&lt;/li&gt;
&lt;li&gt;I try to keep heavy sessions out of peak hours when possible&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Small changes, but the anomaly sessions have mostly stopped showing up.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tool
&lt;/h2&gt;

&lt;p&gt;Open sourced it. Called &lt;strong&gt;ccwhy&lt;/strong&gt;, written in Rust, runs completely offline on your local ~/.claude/ data. No API keys needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;SingggggYee/tap/ccwhy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or: &lt;code&gt;cargo install ccwhy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Or: &lt;a href="https://github.com/SingggggYee/ccwhy/releases" rel="noopener noreferrer"&gt;grab the binary&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's not a replacement for ccusage. ccusage tells you how much you spent. ccwhy tells you why, and what to change.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/SingggggYee/ccwhy" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Curious what other people's breakdowns look like. Is 97% cache reads normal, or is my setup unusually heavy?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>rust</category>
      <category>claude</category>
      <category>claudecode</category>
    </item>
  </channel>
</rss>
