<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Christopher Hiller</title>
    <description>The latest articles on Forem by Christopher Hiller (@boneskull).</description>
    <link>https://forem.com/boneskull</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F177455%2Fe5437457-c853-45d5-8266-f23032471429.png</url>
      <title>Forem: Christopher Hiller</title>
      <link>https://forem.com/boneskull</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/boneskull"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>Christopher Hiller</dc:creator>
      <pubDate>Tue, 02 Sep 2025 20:48:19 +0000</pubDate>
      <link>https://forem.com/boneskull/-hc9</link>
      <guid>https://forem.com/boneskull/-hc9</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/naugtur/sometimes-the-best-thing-to-do-is-an-explicit-nothing-2i71" class="crayons-story__hidden-navigation-link"&gt;Sometimes the best thing to do is an explicit nothing&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/naugtur" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F247817%2Ff7fb04ee-1431-41aa-a06e-52aa5c8a1e47.jpeg" alt="naugtur profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/naugtur" class="crayons-story__secondary fw-medium m:hidden"&gt;
              naugtur
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                naugtur
                
              
              &lt;div id="story-author-preview-content-2802603" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/naugtur" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F247817%2Ff7fb04ee-1431-41aa-a06e-52aa5c8a1e47.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;naugtur&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/naugtur/sometimes-the-best-thing-to-do-is-an-explicit-nothing-2i71" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Aug 27 '25&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/naugtur/sometimes-the-best-thing-to-do-is-an-explicit-nothing-2i71" id="article-link-2802603"&gt;
          Sometimes the best thing to do is an explicit nothing
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/naugtur/sometimes-the-best-thing-to-do-is-an-explicit-nothing-2i71#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Testing in parallel with Mocha v8.0.0</title>
      <dc:creator>Christopher Hiller</dc:creator>
      <pubDate>Wed, 10 Jun 2020 21:03:01 +0000</pubDate>
      <link>https://forem.com/boneskull/testing-in-parallel-with-mocha-v8-0-0-604</link>
      <guid>https://forem.com/boneskull/testing-in-parallel-with-mocha-v8-0-0-604</guid>
      <description>&lt;p&gt;With the &lt;a href="https://github.com/mochajs/mocha/releases/tag/v8.0.0"&gt;release of Mocha v8.0.0&lt;/a&gt;, Mocha now supports running in &lt;em&gt;parallel mode&lt;/em&gt; under Node.js. Running tests in parallel mode allows Mocha to take advantage of multi-core CPUs, resulting in significant speedups for large test suites.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Read about parallel testing in &lt;a href="https://mochajs.org/#parallel-tests"&gt;Mocha’s documentation&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before v8.0.0, Mocha only ran tests in &lt;em&gt;serial&lt;/em&gt;: one test must finish before moving on to the next. While this strategy is not without benefits — it's deterministic and snappy on smaller test suites — it can become a bottleneck when running a large number of tests.&lt;/p&gt;

&lt;p&gt;Let's take a look at how to take advantage of parallel mode in Mocha by enabling it on a real-world project: Mocha itself!&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Node.js v8.0.0 has reached End-of-Life; Mocha v8.0.0 requires Node.js v10, v12, or v14.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mocha doesn’t need to install itself, but you might. You need Mocha v8.0.0 or newer, so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i mocha@8 &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Moving right along...&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the &lt;code&gt;--parallel&lt;/code&gt; flag
&lt;/h2&gt;

&lt;p&gt;In many cases, all you need to do to enable parallel mode is supply &lt;code&gt;--parallel&lt;/code&gt; to the &lt;code&gt;mocha&lt;/code&gt; executable. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mocha &lt;span class="nt"&gt;--parallel&lt;/span&gt; &lt;span class="nb"&gt;test&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.spec.js
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Alternatively, you can specify any command-line flag by using a Mocha &lt;a href="https://mochajs.org/#configuring-mocha-nodejs"&gt;configuration file&lt;/a&gt;. Mocha keeps its default configuration in a YAML file, &lt;code&gt;.mocharc.yml&lt;/code&gt;. It looks something like this (trimmed for brevity):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .mocharc.yml&lt;/span&gt;
&lt;span class="na"&gt;require&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test/setup'&lt;/span&gt;
&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bdd'&lt;/span&gt;
&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To enable parallel mode, I'm going to add &lt;code&gt;parallel: true&lt;/code&gt; to this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .mocharc.yml w/ parallel mode enabled&lt;/span&gt;
&lt;span class="na"&gt;require&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;test/setup'&lt;/span&gt;
&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bdd'&lt;/span&gt;
&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
&lt;span class="na"&gt;parallel&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: Examples below use &lt;code&gt;--parallel&lt;/code&gt; and &lt;code&gt;--no-parallel&lt;/code&gt; for the sake of clarity.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's run &lt;code&gt;npm test&lt;/code&gt; and see what happens!&lt;/p&gt;

&lt;h2&gt;
  
  
  Spoiler: It didn't work the first time
&lt;/h2&gt;

&lt;p&gt;Oops, I got a bunch of "timeout" exceptions in the unit tests, which use the default timeout value (300ms, as shown above). Look:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  2) Mocha
       "before each" hook for "should return the Mocha instance":
     Error: Timeout of 300ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves. (/Users/boneskull/projects/mochajs/mocha/test/node-unit/mocha.spec.js)
      at Hook.Runnable._timeoutError (lib/runnable.js:425:10)
      at done (lib/runnable.js:299:18)
      at callFn (lib/runnable.js:380:7)
      at Hook.Runnable.run (lib/runnable.js:345:5)
      at next (lib/runner.js:475:10)
      at Immediate._onImmediate (lib/runner.js:520:5)
      at processImmediate (internal/timers.js:456:21)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That’s weird. I run the tests a second time, and &lt;em&gt;different&lt;/em&gt; tests throw "timeout" exceptions. Why?&lt;/p&gt;

&lt;p&gt;Because of &lt;em&gt;many&lt;/em&gt; variables — from Mocha to Node.js to the OS to the CPU itself — parallel mode exhibits a much wider range of timings for any given test. These timeout exceptions don't indicate a newfound performance issue; rather, they're a symptom of a naturally higher system load and nondeterministic execution order.&lt;/p&gt;

&lt;p&gt;To resolve this, I'll increase Mocha's default test timeout from 300ms (0.3s) to 1000ms (1s):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .mocharc.yml&lt;/span&gt;
&lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Mocha's "timeout" functionality is &lt;em&gt;not&lt;/em&gt; to be used as a benchmark; its intent is to catch code that takes an unexpectedly long time to execute. Since we now &lt;em&gt;expect&lt;/em&gt; tests to potentially take longer, we can safely increase the &lt;code&gt;timeout&lt;/code&gt; value.&lt;/p&gt;

&lt;p&gt;Now that the tests pass, I'm going to try to make them &lt;em&gt;pass more&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimizing parallel mode
&lt;/h2&gt;

&lt;p&gt;By default, Mocha's maximum job count is &lt;em&gt;n - 1&lt;/em&gt;, where &lt;em&gt;n&lt;/em&gt; is the number of CPU cores on the machine. This default value &lt;em&gt;will not be optimal for all projects&lt;/em&gt;. The job count also does &lt;em&gt;not&lt;/em&gt; imply that "Mocha gets to use &lt;em&gt;n - 1&lt;/em&gt; CPU cores," because that's up to the operating system. It is, however, a default, and it does what defaults do.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I say "&lt;em&gt;maximum&lt;/em&gt; job count" I mean that Mocha &lt;em&gt;could&lt;/em&gt; spawn this many worker processes if needed. It depends on the count and execution time of the test files.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To compare performance, I use the friendly benchmarking tool, &lt;a href="https://github.com/sharkdp/hyperfine"&gt;hyperfine&lt;/a&gt;; I'll use this to get an idea of how various configurations will perform.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Regarding &lt;code&gt;hyperfine&lt;/code&gt; usage:&lt;/p&gt;

&lt;p&gt;In the below examples, I’m passing two options to &lt;code&gt;hyperfine&lt;/code&gt;: &lt;code&gt;-r 5&lt;/code&gt; for "runs," which runs the command five (5) times. The default is ten (10), but this is slow, and I'm impatient. The second option I pass is &lt;code&gt;--warmup 1&lt;/code&gt;, which performs a single "warmup" run. The result of this run is discarded. Warmup runs reduce the chance that the first &lt;em&gt;k&lt;/em&gt; runs will be significantly slower than the subsequent runs, which may skew the final result. If this happens, &lt;code&gt;hyperfine&lt;/code&gt; will even warn you about it, which is why I'm using it!&lt;br&gt;&lt;br&gt;
If you try this yourself, you need to replace &lt;code&gt;bin/mocha&lt;/code&gt; with &lt;code&gt;node_modules/.bin/mocha&lt;/code&gt; or &lt;code&gt;mocha&lt;/code&gt;, depending on your environment; &lt;code&gt;bin/mocha&lt;/code&gt; is the path to the &lt;code&gt;mocha&lt;/code&gt; executable relative to the working copy root.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mocha's integration tests (about 260 tests over 55 files) typically make assertions about the output of the &lt;code&gt;mocha&lt;/code&gt; executable itself. They also need a longer &lt;code&gt;timeout&lt;/code&gt; value than the unit tests; below, we use a timeout of ten (10) seconds.&lt;/p&gt;

&lt;p&gt;I run the integration tests in serial. Nobody ever claimed they ran at &lt;em&gt;ludicrous speed&lt;/em&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hyperfine &lt;span class="nt"&gt;-r&lt;/span&gt; 5 &lt;span class="nt"&gt;--warmup&lt;/span&gt; 1 &lt;span class="s2"&gt;"bin/mocha --no-parallel --timeout &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
10s test/integration/**/*.spec.js"&lt;/span&gt;
Benchmark &lt;span class="c"&gt;#1: bin/mocha --no-parallel --timeout 10s test/integration/**/*.spec.js&lt;/span&gt;
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:     141.873 s ±  0.315 s    &lt;span class="o"&gt;[&lt;/span&gt;User: 72.444 s, System: 14.836 s]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:   141.447 s … 142.296 s    5 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;That's over two (2) minutes. Let’s try it again in parallel mode. In my case, I have an eight-core CPU (&lt;code&gt;n = 8&lt;/code&gt;), so by default, Mocha uses seven (7) worker processes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hyperfine &lt;span class="nt"&gt;-r&lt;/span&gt; 5 &lt;span class="nt"&gt;--warmup&lt;/span&gt; 1 &lt;span class="s2"&gt;"bin/mocha --parallel --timeout 10s &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
test/integration/**/*.spec.js"&lt;/span&gt;
Benchmark &lt;span class="c"&gt;#1: bin/mocha --parallel --timeout 10s test/integration/**/*.spec.js&lt;/span&gt;
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:     65.235 s ±  0.191 s    &lt;span class="o"&gt;[&lt;/span&gt;User: 78.302 s, System: 16.523 s]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:   65.002 s … 65.450 s    5 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Using parallel mode shaves 76 seconds off of the run, down to just over a minute! That's almost a 53% speedup. But, can we do better?&lt;/p&gt;

&lt;p&gt;I can use the &lt;code&gt;--jobs/-j&lt;/code&gt; option to specify &lt;em&gt;exactly&lt;/em&gt; how many worker processes Mocha will potentially use. Let's see what happens if I &lt;em&gt;reduce&lt;/em&gt; this number to four (4):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hyperfine &lt;span class="nt"&gt;-r&lt;/span&gt; 5 &lt;span class="nt"&gt;--warmup&lt;/span&gt; 1 &lt;span class="s2"&gt;"bin/mocha --parallel --jobs 4 --timeout 10s &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
test/integration/**/*.spec.js"&lt;/span&gt;
Benchmark &lt;span class="c"&gt;#1: bin/mocha --parallel --jobs 4 --timeout 10s \&lt;/span&gt;
&lt;span class="nb"&gt;test&lt;/span&gt;/integration/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.spec.js
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:     69.764 s ±  0.512 s    &lt;span class="o"&gt;[&lt;/span&gt;User: 79.176 s, System: 16.774 s]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:   69.290 s … 70.597 s    5 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unfortunately, that's slower. What if I &lt;em&gt;increased&lt;/em&gt; the number of jobs, instead?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hyperfine &lt;span class="nt"&gt;-r&lt;/span&gt; 5 &lt;span class="nt"&gt;--warmup&lt;/span&gt; 1 &lt;span class="s2"&gt;"bin/mocha --parallel --jobs 12 --timeout 10s &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
test/integration/**/*.spec.js"&lt;/span&gt;
Benchmark &lt;span class="c"&gt;#1: bin/mocha --parallel --jobs 12 --timeout 10s test/integration/**/*.spec.js&lt;/span&gt;
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:     64.175 s ±  0.248 s    &lt;span class="o"&gt;[&lt;/span&gt;User: 80.611 s, System: 17.109 s]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:   63.809 s … 64.400 s    5 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Twelve (12) is ever-so-slightly faster than the default of seven (7). Remember, my CPU has eight (8) cores. Why does spawning &lt;em&gt;more&lt;/em&gt; processes increase performance?&lt;/p&gt;

&lt;p&gt;I speculate it's because these tests aren't CPU-bound. They mostly perform asynchronous I/O, so the CPU has some spare cycles waiting for tasks to complete. I could spend more time trying to squeeze another 500ms out of these tests, but for my purposes, it's not worth the bother. Perfect is the enemy of good, right?  The point is to illustrate how you can apply this strategy to your own projects and arrive at a configuration you're happy with.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to avoid parallel mode
&lt;/h2&gt;

&lt;p&gt;Would you be shocked if I told you that running tests in parallel is &lt;em&gt;not always appropriate?&lt;/em&gt; No, you would not be shocked.&lt;/p&gt;

&lt;p&gt;It's important to understand two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mocha does not run individual tests in parallel&lt;/strong&gt;. Mocha runs &lt;em&gt;test files&lt;/em&gt; in parallel.&lt;/li&gt;
&lt;li&gt;Spawning worker processes is not free.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That means if you hand Mocha &lt;em&gt;a single, lonely test file&lt;/em&gt;, it will spawn &lt;em&gt;a single worker process&lt;/em&gt;, and that worker process will run the file. &lt;em&gt;If you only have one test file, you'll be penalized for using parallel mode.&lt;/em&gt; Don't do that.&lt;/p&gt;

&lt;p&gt;Other than the "lonely file" non-use-case, the unique characteristics of your tests and sources will impact the result. There's an inflection point below which running tests in parallel will be &lt;em&gt;slower&lt;/em&gt; than running in serial.&lt;/p&gt;

&lt;p&gt;In fact, Mocha's own unit tests (about 740 tests across 35 files) are a great example. Like good unit tests, they try to run quickly, in isolation, without I/O. I'll run Mocha's unit tests in serial, for the baseline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hyperfine &lt;span class="nt"&gt;-r&lt;/span&gt; 5 &lt;span class="nt"&gt;--warmup&lt;/span&gt; 1 &lt;span class="s2"&gt;"bin/mocha --no-parallel test/*unit/**/*.spec.js"&lt;/span&gt;
Benchmark &lt;span class="c"&gt;#1: bin/mocha --no-parallel test/*unit/**/*.spec.js&lt;/span&gt;
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:      1.262 s ±  0.026 s    &lt;span class="o"&gt;[&lt;/span&gt;User: 1.286 s, System: 0.145 s]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:    1.239 s …  1.297 s    5 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now I'll try running them in parallel. Despite my hopes, this is the result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;hyperfine &lt;span class="nt"&gt;-r&lt;/span&gt; 5 &lt;span class="nt"&gt;--warmup&lt;/span&gt; 1 &lt;span class="s2"&gt;"bin/mocha --parallel test/*unit/**/*.spec.js"&lt;/span&gt;
Benchmark &lt;span class="c"&gt;#1: bin/mocha --parallel test/*unit/**/*.spec.js&lt;/span&gt;
  Time &lt;span class="o"&gt;(&lt;/span&gt;mean ± σ&lt;span class="o"&gt;)&lt;/span&gt;:      1.718 s ±  0.023 s    &lt;span class="o"&gt;[&lt;/span&gt;User: 3.443 s, System: 0.619 s]
  Range &lt;span class="o"&gt;(&lt;/span&gt;min … max&lt;span class="o"&gt;)&lt;/span&gt;:    1.686 s …  1.747 s    5 runs
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Objectively, running Mocha's unit tests in parallel &lt;em&gt;slows them down&lt;/em&gt; by about half a second. This is the overhead of spawning worker processes (and the requisite serialization for inter-process communication). &lt;/p&gt;

&lt;p&gt;I'll go out on a limb and predict that &lt;em&gt;many projects&lt;/em&gt; having very &lt;em&gt;fast unit tests&lt;/em&gt; will &lt;em&gt;see no benefit&lt;/em&gt; from running those tests in Mocha's parallel mode.&lt;/p&gt;

&lt;p&gt;Remember my &lt;code&gt;.mocharc.yml&lt;/code&gt;?  I yanked that &lt;code&gt;parallel: true&lt;/code&gt; out of there; instead, Mocha will use it only when running its integration tests.&lt;/p&gt;

&lt;p&gt;In addition to being generally unsuitable for these types of tests, parallel mode has some other limitations; I'll discuss these next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats, disclaimers and gotchas, Oh my
&lt;/h2&gt;

&lt;p&gt;Due to technical limitations (i.e., "reasons"), a handful of features aren't compatible with parallel mode. If you try, Mocha will throw an exception.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://mochajs.org/#parallel-tests"&gt;Check out the docs&lt;/a&gt; for more information and workarounds (if any).&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Unsupported reporters
&lt;/h3&gt;

&lt;p&gt;If you're using the &lt;code&gt;markdown&lt;/code&gt;, &lt;code&gt;progress&lt;/code&gt;, or &lt;code&gt;json-stream&lt;/code&gt; reporters, you're out of luck for now. These reporters need to know how many tests we intend to execute &lt;em&gt;up front&lt;/em&gt;, and parallel mode does not have that information.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This could change in the future, but would introduce breaking changes to these reporters.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Exclusive tests
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://mochajs.org/#exclusive-tests"&gt;Exclusive tests&lt;/a&gt; (&lt;code&gt;.only()&lt;/code&gt;) do not work. If you try, Mocha runs tests (as if &lt;code&gt;.only()&lt;/code&gt; was not used) until it encounters usage of &lt;code&gt;.only()&lt;/code&gt;, at which point it aborts and fails.&lt;/p&gt;

&lt;p&gt;Given that exclusive tests are typically used in a single file, parallel mode is &lt;em&gt;also&lt;/em&gt; unsuitable for this situation. &lt;/p&gt;

&lt;h3&gt;
  
  
  Unsupported options
&lt;/h3&gt;

&lt;p&gt;Incompatible options include &lt;code&gt;--sort&lt;/code&gt;, &lt;code&gt;--delay&lt;/code&gt;, and importantly, &lt;code&gt;--file&lt;/code&gt;. In short, it's because we cannot run tests in any specific order.&lt;/p&gt;

&lt;p&gt;Of these, &lt;code&gt;--file&lt;/code&gt; likely impacts the greatest number of projects. Before Mocha v8.0.0, &lt;code&gt;--file&lt;/code&gt; was recommended to define "root hooks." Root hooks are hooks (such as &lt;code&gt;beforeEach()&lt;/code&gt;, &lt;code&gt;after()&lt;/code&gt;, &lt;code&gt;setup()&lt;/code&gt;, etc.) which all other test files will inherit. The idea is that you would define root hooks in, for example, &lt;code&gt;hooks.js&lt;/code&gt;, and run Mocha like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;mocha &lt;span class="nt"&gt;--file&lt;/span&gt; hooks.js &lt;span class="s2"&gt;"test/**/*.spec.js"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All &lt;code&gt;--file&lt;/code&gt; parameters are considered test files and will be run &lt;em&gt;in order&lt;/em&gt; and &lt;em&gt;before any other test files&lt;/em&gt; (in this case, &lt;code&gt;test/**/*.spec.js&lt;/code&gt;). Because of these guarantees, Mocha "bootstraps" with the hooks defined in &lt;code&gt;hooks.js&lt;/code&gt;, and this affects all subsequent test files.&lt;/p&gt;

&lt;p&gt;This still works in Mocha v8.0.0, but &lt;em&gt;only&lt;/em&gt; in serial mode. But wait!  Its use is now &lt;em&gt;strongly discouraged&lt;/em&gt; (and will eventually be fully deprecated). In its place, Mocha has introduced Root Hook Plugins.&lt;/p&gt;

&lt;h2&gt;
  
  
  Root Hook Plugins
&lt;/h2&gt;

&lt;p&gt;Root Hook Plugins are modules (CJS or ESM) which have a named export, &lt;code&gt;mochaHooks&lt;/code&gt;, in which the user can freely define hooks. Root Hook Plugin modules are loaded via Mocha's &lt;code&gt;--require&lt;/code&gt; option.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&lt;a href="https://mochajs.org/#root-hook-plugins"&gt;Read the docs on using Root Hook Plugins&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The documentation (linked above) contains a thorough explanation and more examples, but here’s a straightforward one.&lt;/p&gt;

&lt;p&gt;Say you have a project with root hooks loaded via &lt;code&gt;--file hooks.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// hooks.js&lt;/span&gt;
&lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// do something before every test&lt;/span&gt;
  &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// trivial example&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To convert this to a Root Hook Plugin, change &lt;code&gt;hooks.js&lt;/code&gt; to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// hooks.js&lt;/span&gt;
&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mochaHooks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Tip: This can be an ESM module, for example, &lt;code&gt;hooks.mjs&lt;/code&gt;; use a named export of &lt;code&gt;mochaHooks&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When calling the &lt;code&gt;mocha&lt;/code&gt; executable, replace &lt;code&gt;--file hooks.js&lt;/code&gt; with &lt;code&gt;--require hooks.js&lt;/code&gt;. Nifty!&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting parallel mode
&lt;/h2&gt;

&lt;p&gt;While parallel mode should &lt;em&gt;just work&lt;/em&gt; for many projects, if you're still having trouble, refer to this checklist to prepare your tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Ensure you are using a &lt;a href="https://mochajs.org#reporter-limitations"&gt;supported reporter&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;✅ Ensure you are not using &lt;a href="https://mochajs.org#file-order-is-non-deterministic"&gt;other unsupported flags&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;✅ Double-check your &lt;a href="https://mochajs.org/#configuring-mocha-nodejs"&gt;config file&lt;/a&gt;; options set in config files will be merged with any command-line option.&lt;/li&gt;
&lt;li&gt;✅ Look for &lt;em&gt;root hooks&lt;/em&gt; (they look like &lt;a href="https://mochajs.org/#root-hooks-are-not-global"&gt;this&lt;/a&gt;) in your tests. Move them into a &lt;a href="https://mochajs.org/#root-hook-plugins"&gt;root hook plugin&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;✅ Do any assertion, mock, or other test libraries you're consuming use root hooks? They may need to be &lt;a href="https://mochajs.org/#migrating-a-library-to-use-root-hook-plugins"&gt;migrated&lt;/a&gt; for compatibility with parallel mode.&lt;/li&gt;
&lt;li&gt;✅ If tests are unexpectedly timing out, you may need to increase the default test timeout (via &lt;a href="https://mochajs.org/#-timeout-ms-t-ms"&gt;&lt;code&gt;--timeout&lt;/code&gt;&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;✅ Ensure your tests do not depend on being run in a specific order.&lt;/li&gt;
&lt;li&gt;✅ Ensure your tests clean up after themselves; remove temp files, handles, sockets, etc. Don't try to share state or resources between test files.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Parallel mode is &lt;em&gt;new&lt;/em&gt; and &lt;em&gt;not perfect&lt;/em&gt;; there's room for improvement. But to do so, Mocha needs your help. Send the Mocha team &lt;a href="https://github.com/mochajs/mocha/issues"&gt;your feedback&lt;/a&gt;! Please give &lt;a href="https://github.com/mochajs/mocha/releases/tag/v8.0.0"&gt;Mocha v8.0.0&lt;/a&gt; a try, enable parallel mode, use Root Hook Plugins, and share your thoughts.&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>testing</category>
    </item>
    <item>
      <title>Introducing report-toolkit for Node.js Diagnostic Reports</title>
      <dc:creator>Christopher Hiller</dc:creator>
      <pubDate>Mon, 06 Jan 2020 18:51:11 +0000</pubDate>
      <link>https://forem.com/boneskull/introducing-report-toolkit-for-node-js-diagnostic-reports-3e74</link>
      <guid>https://forem.com/boneskull/introducing-report-toolkit-for-node-js-diagnostic-reports-3e74</guid>
      <description>&lt;p&gt;In this article, I introduce you to &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt;, show you its coolest features, and help you get up and running with the new technology. Since Diagnostic Reports is a relatively new feature in Node.js and is still considered experimental, I'll start with a brief overview.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js Diagnostic Reports: The basics
&lt;/h2&gt;

&lt;p&gt;In this section, I explain what Diagnostic Reports are and how to generate them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a more in-depth overview, check out &lt;a href="https://developer.ibm.com/articles/easily-identify-problems-in-your-nodejs-apps-with-diagnostic-report/"&gt;Easily Identify Problems in your Node.js Apps with Diagnostic Report&lt;/a&gt;, written by Gireesh Punathil, a Node.js TSC member.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What are Node.js Diagnostic Reports?
&lt;/h3&gt;

&lt;p&gt;Originally introduced in Node.js v11.7.0 as an experimental feature, a Diagnostic Report is a JSON file (or JavaScript object) containing a diagnostic summary of a Node.js process. Diagnostic Reports are especially helpful for post-mortem debugging or situations in which it's impractical to attach a debugger.&lt;/p&gt;

&lt;p&gt;A Diagnostic Report's summary contains information about the state of a &lt;code&gt;node&lt;/code&gt; process, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Process information (static information which lives in the &lt;a href="https://nodejs.org/api/process.html#process_process"&gt;Process object&lt;/a&gt;) including the version of Node.js and the versions of its dependencies&lt;/li&gt;
&lt;li&gt;Operating system, platform, and CPU information&lt;/li&gt;
&lt;li&gt;The state of the JavaScript and native (C++) stacks&lt;/li&gt;
&lt;li&gt;Memory and resource usage&lt;/li&gt;
&lt;li&gt;The state of the &lt;code&gt;libuv&lt;/code&gt; event loop&lt;/li&gt;
&lt;li&gt;Environment variables&lt;/li&gt;
&lt;li&gt;Shared libraries&lt;/li&gt;
&lt;li&gt;Metadata about the report file itself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As of this writing (the current version of Node.js is v13.5.0), Diagnostic Reports is still considered experimental. Practically speaking, that means the usage, API, or output may introduce &lt;em&gt;breaking changes at any time&lt;/em&gt; in both the LTS (v12.x) and current (v13.x) release lines.&lt;/p&gt;

&lt;p&gt;That being said, the underlying implementation has proved itself robust in the months since its arrival, and I don’t expect it to be a source of problems for early adopters.&lt;/p&gt;

&lt;p&gt;Next, I show you the &lt;em&gt;magic spell&lt;/em&gt; you need to generate a report. And by &lt;em&gt;magic spell&lt;/em&gt;, I mean command-line flags.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I generate a Node.js Diagnostic Report?
&lt;/h3&gt;

&lt;p&gt;For this section, I recommend you use a version of Node.js between v12.5.0 and v13.1.0, inclusive. The API and output has not changed within this range. Newer versions may break the following assumptions, due to Diagnostic Reports' experimental status.&lt;/p&gt;

&lt;p&gt;As with other experimental features, you need to supply &lt;code&gt;node&lt;/code&gt; a flag. That flag is &lt;code&gt;--experimental-report&lt;/code&gt;. In your shell, use it like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-report&lt;/span&gt; &amp;lt;my-script.js&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The above command configures the &lt;code&gt;node&lt;/code&gt; process to expose the global &lt;code&gt;process.report&lt;/code&gt; API, and allows you to use other command-line flags to configure automatic report generation.&lt;/p&gt;

&lt;p&gt;The most straightforward way to create a Diagnostic Report is to use the API. You can use &lt;code&gt;node&lt;/code&gt; to run an inline script which calls &lt;code&gt;process.report.writeReport()&lt;/code&gt;, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-report&lt;/span&gt; &lt;span class="nt"&gt;--eval&lt;/span&gt; &lt;span class="s2"&gt;"process.report.writeReport('report.json')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You will see console output (on &lt;code&gt;STDERR&lt;/code&gt;) like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Writing Node.js report to file: report.json
Node.js report completed
(node:66875) ExperimentalWarning: report is an experimental feature. This feature could change at any time
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;A file, &lt;code&gt;report.json&lt;/code&gt;, will now live in your current directory. Open it in your editor, take a gander, and ponder the arcane wisdom contained therein.&lt;/p&gt;

&lt;p&gt;You should now have a basic grasp of the &lt;em&gt;what&lt;/em&gt;, &lt;em&gt;why&lt;/em&gt; and &lt;em&gt;how&lt;/em&gt; of Diagnostic Reports in Node.js. Given these fundamentals, you’ll better understand the motivation behind &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing report-toolkit for real this time
&lt;/h2&gt;

&lt;p&gt;While Diagnostic Reports are useful in solving a certain class of problems, the reports themselves can be impenetrable and tedious to work with. With feedback from the Node.js community, I designed &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt; to pave over the common speedbumps, and arrive more quickly at solutions.&lt;/p&gt;

&lt;p&gt;report-toolkit is packaged as a command-line app (&lt;code&gt;rtk&lt;/code&gt;), providing subcommands (think &lt;code&gt;git&lt;/code&gt;) which map to a set of unique, purpose-built features. I’m going to start with what I believe is the &lt;em&gt;most rad&lt;/em&gt; feature: automated analysis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analyze Diagnostic Reports with report-toolkit
&lt;/h3&gt;

&lt;p&gt;Deep in the forest of a production filesystem, a developer happens upon a Diagnostic Report file. Taking stock of the surroundings, the developer discovers &lt;em&gt;a process is not running&lt;/em&gt;. Experience and reasoning leads the developer to deduce, &lt;em&gt;“These are the remains of a Node.js process.”&lt;/em&gt;  &lt;/p&gt;

&lt;p&gt;What happened here? How did this process meet its untimely demise? &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt; can help with the investigation.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rtk&lt;/code&gt;’s &lt;code&gt;inspect&lt;/code&gt; feature runs a set of &lt;em&gt;rules&lt;/em&gt; on one or more reports.  Each rule is a function, and aims to provide analysis which is “good enough” — in other words, these are heuristics.  If a function finds something bad, smelly, or otherwise dubious, &lt;code&gt;rtk inspect&lt;/code&gt; will report this to the developer.&lt;/p&gt;

&lt;p&gt;These rules are inspired by &lt;a href="https://eslint.org"&gt;ESLint&lt;/a&gt;’s concept of a rule.  Each rule is modular, each rule is configurable, and best of all: you can write your own and share!&lt;/p&gt;

&lt;p&gt;report-toolkit ships with a small set of built-in rules to recognize some known problems — these include CPU and resource usage thresholds, long timeouts in the event loop, and mismatched shared library versions. As more developers adopt Diagnostic Reports (and, hopefully, &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt;) to solve problems, we aim to ship widely applicable heuristics as they are uncovered by the community. For other more environment-specific needs, &lt;code&gt;rtk&lt;/code&gt; can use heuristics published as third-party modules (as “plugins”) or even just a script on disk.&lt;/p&gt;

&lt;p&gt;Let's look at &lt;code&gt;rtk inspect&lt;/code&gt; in action.&lt;/p&gt;

&lt;p&gt;The following image is the output from running &lt;code&gt;rtk inspect&lt;/code&gt; on a report file which notes that the system shared libraries in use by &lt;code&gt;openssl&lt;/code&gt; (&lt;code&gt;libcrypto&lt;/code&gt; and &lt;code&gt;libssl&lt;/code&gt;) &lt;em&gt;are not from the version of &lt;code&gt;openssl&lt;/code&gt; which Node.js expects&lt;/em&gt;. It expects &lt;code&gt;v1.1.1b&lt;/code&gt;, but it has been compiled against &lt;code&gt;v1.1&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TpwvxzQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zbcy1jcon90akm5s84x7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TpwvxzQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zbcy1jcon90akm5s84x7.png" alt="Example of error note in report-toolkit"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As another example, the &lt;code&gt;rtk inspect&lt;/code&gt; element shows a Diagnostic Report that indicates suspiciously high CPU usage:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BJf2wLyr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wp8g9t5asekuutl47m44.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BJf2wLyr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/wp8g9t5asekuutl47m44.png" alt="High CPU usage error"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The above check takes the number of CPU cores into account, which otherwise would require the developer to do &lt;em&gt;math&lt;/em&gt;, and nobody wants to do that.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Please stay tuned for tutorials on creating your own plugins!  &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The next feature we'll look at is less flashy, but extremely helpful: &lt;em&gt;redaction of secrets&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automatically redact secrets from Diagnostic Reports using report-toolkit
&lt;/h3&gt;

&lt;p&gt;As mentioned above, Diagnostic Report contains &lt;em&gt;the entire contents of your environment variables.&lt;/em&gt; This includes things like cloud provider API keys, tokens, session IDs, etc. The data is in a &lt;code&gt;.json&lt;/code&gt; file, and as we are all careful and security-conscious developers, we must take great care of how and where we transmit this file.&lt;/p&gt;

&lt;p&gt;You may be tempted to hand-edit these secrets out of the file, but&lt;br&gt;
&lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt; provides a &lt;code&gt;redact&lt;/code&gt; feature, which — as you may have guessed — &lt;em&gt;redacts&lt;/em&gt; commonly known secrets and phrases from the environment variables in a Diagnostic Report file.  &lt;/p&gt;

&lt;p&gt;Secrets include those used by major cloud providers, including IBM Cloud, AWS, Azure, and Google Cloud.&lt;/p&gt;

&lt;p&gt;If you like to look at regular expressions, you can &lt;a href="https://github.com/IBM/report-toolkit/blob/master/packages/common/src/redact.js"&gt;take a look&lt;/a&gt; at the matching.&lt;/p&gt;

&lt;p&gt;Instead of obliterating the key outright, the value is replaced with the string &lt;code&gt;[REDACTED]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's look at this feature in action. Take a Diagnostic Report containing the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environmentVariables"&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;"CLOUD_PROVIDER_ACCESS_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MY_SUPER_SECRET_KEY"&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;Now, run &lt;code&gt;rtk redact /path/to/report.json&lt;/code&gt;. &lt;code&gt;rtk&lt;/code&gt; will dump the entire report to your terminal. The result will contain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environmentVariables"&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;"CLOUD_PROVIDER_ACCESS_KEY"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"[REDACTED]"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I recommend using the &lt;code&gt;--replace&lt;/code&gt; option to &lt;code&gt;rtk redact&lt;/code&gt;, which overwrites the file &lt;em&gt;in-place&lt;/em&gt; instead of printing to &lt;code&gt;STDOUT&lt;/code&gt;. If you’re leery about that, try &lt;code&gt;--output &amp;lt;new-filename&amp;gt;&lt;/code&gt;, which will create a new file from the redacted Diagnostic Report.&lt;/p&gt;

&lt;p&gt;Another task &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt; helps with is &lt;em&gt;comparison&lt;/em&gt; of Diagnostic Reports.&lt;/p&gt;

&lt;h3&gt;
  
  
  Comparing Diagnostic Reports with report-toolkit
&lt;/h3&gt;

&lt;p&gt;Suppose you have two Diagnostic Reports generated from a single &lt;code&gt;node&lt;/code&gt; process. Or two reports generated from the same script, but on different machines. Or two reports from different processes on the same machine. Or whatever—&lt;em&gt;you have two reports, OK?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; use &lt;code&gt;diff report-1.json report-2.json&lt;/code&gt;. Or diff it in vim or some other GUI tool. That will (eventually) get the job done. But these tools weren’t made to compare Diagnostic Reports; they were made to compare arbitrary text files and source code.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rtk&lt;/code&gt;, on the other hand, provides a &lt;code&gt;diff&lt;/code&gt; command &lt;em&gt;purpose-built&lt;/em&gt; for Diagnostic Reports. It hides generally uninteresting information (certain timestamps and JSON syntax) and provides a helpful side-by-side view, noting what has been added, modified, or removed between the left and right reports.&lt;/p&gt;

&lt;p&gt;Example output from &lt;code&gt;rtk diff &amp;lt;report-1.json&amp;gt; &amp;lt;report-2.json&amp;gt;&lt;/code&gt; looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4n0KGYXH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/r0db32djaup5r31y5mdn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4n0KGYXH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/r0db32djaup5r31y5mdn.png" alt='Example "diff" output'&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above, note the differences between &lt;code&gt;header.processId&lt;/code&gt; (good for checking if the same process created both reports), the current working directory, the command-line flags to &lt;code&gt;node&lt;/code&gt;, and finally the different versions of Node.js used to create the reports.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rtk diff&lt;/code&gt; allows explicit including and excluding of field names (using “dot” syntax), in case there’s something you want to zero in on, or ignore; &lt;code&gt;rtk diff report-1.json report-2.json -i header.nodejsVersion&lt;/code&gt; would only compare the Node.js version values.&lt;/p&gt;

&lt;p&gt;Having worked with Diagnostic Report files, Alice &amp;amp; Bob notice the files contain &lt;em&gt;much JSON&lt;/em&gt;; each is around 25KiB. Alice and Bob may not need &lt;em&gt;all&lt;/em&gt; that information, or maybe they need it in a different format; this is a problem &lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt; helps solve via &lt;em&gt;transformers&lt;/em&gt;.  Read on, Alice and Bob!&lt;/p&gt;

&lt;h3&gt;
  
  
  Transformation of Diagnostic Reports with report-toolkit
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Transformers&lt;/em&gt;, in report-toolkit parlance, can be thought of as “mapping” functions. When performing a transformation, &lt;code&gt;report-toolkit&lt;/code&gt; starts with a Diagnostic Report, maps it through &lt;em&gt;one or more&lt;/em&gt; transformers, and finally produces output.&lt;/p&gt;

&lt;p&gt;report-toolkit ships with a handful of built-in transformers intended for general-purpose use. However, I’d like to train the spotlight on a transformer tailored to a specific use case: identification of unique uncaught exceptions.&lt;/p&gt;

&lt;p&gt;When an uncaught exception is thrown in Node.js, best practices &lt;a href="https://nodejs.org/api/process.html#process_warning_using_uncaughtexception_correctly"&gt;recommend&lt;/a&gt; that the process does not attempt to resume normal operation. Instead, it should exit. A typical recovery strategy, then, is to just &lt;em&gt;restart the process.&lt;/em&gt; The service is back online, and a developer can begin a post-mortem investigation as appropriate.&lt;/p&gt;

&lt;p&gt;To aid in post-mortem debugging, Node.js can be configured to &lt;em&gt;automatically generate&lt;/em&gt; a report in the case of an uncaught exception (using &lt;code&gt;--report-uncaught-exception&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The reason behind any given uncaught exception may very well be a bug — but it also might be due to circumstances outside of developer control, such as network outages or Martian invasion. By examining the Diagnostic Report and its stack trace, a developer can identify an exception as “known” or, less charitably, “somebody else’s problem.”  &lt;/p&gt;

&lt;p&gt;Yet, this doesn’t answer the questions: “How many of these are out of my control and how often?” or “How many JIRA tickets do I need to create and assign to myself?”&lt;/p&gt;

&lt;p&gt;To count chickens, one must know a chicken.&lt;/p&gt;

&lt;p&gt;report-toolkit can help developers count chickens using the &lt;code&gt;stack-hash&lt;/code&gt; transformer. This transformer computes a SHA1 hash of the exception—establishing &lt;em&gt;unique&lt;/em&gt; exceptions — and outputs the hash along with the complete stack trace. Pipe this into a data store, and now you’ve got some lovely metrics for your favorite dashboard.&lt;/p&gt;

&lt;p&gt;The output looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dumpEventTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2019-11-21T15:18:47Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"filepath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"report.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;"message"&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: your problem"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sha1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9c1d91a8e0f6944e0c0bc920c55e64145c3823a8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stack"&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;"at Object.&amp;lt;anonymous&amp;gt; (/path/to/script.js:1:7)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"at Module._compile (internal/modules/cjs/loader.js:956:30)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"at Module.load (internal/modules/cjs/loader.js:812:32)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"at Function.Module._load (internal/modules/cjs/loader.js:724:14)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"at Function.Module.runMain (internal/modules/cjs/loader.js:1025:10)"&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;In a future release, report-toolkit will allow a user to customize which information is used to compute the hash.&lt;/p&gt;

&lt;p&gt;We've just scratched the surface of transformers in report-toolkit. To learn more — and see a list of built-in transformers — &lt;a href="https://ibm.github.io/report-toolkit/quick-start#transforming-a-report"&gt;check out report-toolkit’s quick start guide&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Ideally, this article gave you an understanding of the fundamentals of Diagnostic Reports in Node.js, and the ways in which report-toolkit can help you use them more effectively to solve problems. Yes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give feedback about report-toolkit
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://ibm.github.io/report-toolkit"&gt;report-toolkit&lt;/a&gt; is a &lt;em&gt;brand new&lt;/em&gt; (announced October 2019) Apache-2.0-licensed OSS project from IBM, created and maintained by me, &lt;a href="https://github.com/boneskull"&gt;Christopher “boneskull” Hiller&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While I'm busy writing more docs, I hope you can give it a try — I’d love your feedback.&lt;br&gt;
These are my questions for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What worked well?&lt;/li&gt;
&lt;li&gt;What didn’t work well? How could it be better?&lt;/li&gt;
&lt;li&gt;Found a bug? Any feature requests?&lt;/li&gt;
&lt;li&gt;Other questions?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please drop an issue in &lt;a href="https://github.com/IBM/report-toolkit/issues"&gt;report-toolkit's issue tracker&lt;/a&gt;. All contributions are welcome!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article originally appeared December 19, 2019 on &lt;a href="https://developer.ibm.com/articles/introducing-report-toolkit-for-nodejs-diagnostic-reports/"&gt;developer.ibm.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>debugging</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Recursive Directory Removal in Node.js</title>
      <dc:creator>Christopher Hiller</dc:creator>
      <pubDate>Mon, 09 Sep 2019 17:14:50 +0000</pubDate>
      <link>https://forem.com/boneskull/recursive-directory-removal-in-node-js-239f</link>
      <guid>https://forem.com/boneskull/recursive-directory-removal-in-node-js-239f</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--BovOwTWy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://boneskull.com/content/images/2019/09/Sierpinsky_triangle_-evolution-.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--BovOwTWy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://boneskull.com/content/images/2019/09/Sierpinsky_triangle_-evolution-.png" alt="Recursive Directory Removal in Node.js"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Recursive directory removal &lt;a href="https://github.com/nodejs/node/pull/29168"&gt;has landed&lt;/a&gt; in Node.js v12.10.0!&lt;/p&gt;

&lt;p&gt;This has been a long-standing feature request.  New Node.js developers often express incredulity  when they discover this particular “battery” isn’t included in Node.js.&lt;/p&gt;

&lt;p&gt;Over the years, userland modules (&lt;a href="https://npm.im/rimraf"&gt;&lt;strong&gt;rimraf&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://npm.im/rmdir"&gt;&lt;strong&gt;rmdir&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://npm.im/del"&gt;&lt;strong&gt;del&lt;/strong&gt;&lt;/a&gt;, &lt;a href="https://npm.im/fs-extra"&gt;&lt;strong&gt;fs-extra&lt;/strong&gt;&lt;/a&gt;, etc.) have heroically provided what core did not. Thanks to the superbad maintainers-of and contributors-to these packages!&lt;/p&gt;

&lt;p&gt;Here's a little story about how it came to pass—and why something &lt;em&gt;so seemingly simple&lt;/em&gt; as &lt;code&gt;rm -rf&lt;/code&gt; isn’t necessarily so.&lt;/p&gt;

&lt;h2&gt;
  
  
  About Node.js’ Filesystem Operations
&lt;/h2&gt;

&lt;p&gt;First, I want to explain a bit about how Node.js works under the hood with regards to filesystem operations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://libuv.org"&gt;&lt;strong&gt;libuv&lt;/strong&gt;&lt;/a&gt; provides filesystem operations to Node.js.  Node.js’ &lt;code&gt;fs&lt;/code&gt; module is just a JavaScript file which provides the &lt;code&gt;fs.*&lt;/code&gt; APIs; those APIs call into an internal C++ binding (you could think of this as a “native module”). That binding is &lt;em&gt;glue&lt;/em&gt; between &lt;strong&gt;libuv&lt;/strong&gt; and the JavaScript engine ( &lt;strong&gt;V8&lt;/strong&gt; ).&lt;/p&gt;

&lt;p&gt;Here’s an example.  At the lowest level, &lt;strong&gt;libuv&lt;/strong&gt; provides a C API (&lt;code&gt;uv_fs_rmdir&lt;/code&gt;) to make the a system call to remove a directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// `rmdir` is just a function which calls into a C++ binding.&lt;/span&gt;
&lt;span class="c1"&gt;// The binding asks libuv to remove the "/tmp/foo" directory.&lt;/span&gt;
&lt;span class="c1"&gt;// Once libuv returns a result, the binding calls `callback`&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rmdir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/tmp/foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handle error&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;Importantly, Node.js makes only a &lt;em&gt;single call&lt;/em&gt; to &lt;strong&gt;libuv&lt;/strong&gt; above_._&lt;/p&gt;

&lt;p&gt;In fact, until recently, Node.js’ &lt;code&gt;fs&lt;/code&gt; bindings follow a pattern: single calls into &lt;strong&gt;libuv&lt;/strong&gt;. &lt;code&gt;fs.readFile&lt;/code&gt;, &lt;code&gt;fs.stat&lt;/code&gt;, &lt;code&gt;fs.unlink&lt;/code&gt;; these are all just &lt;em&gt;one&lt;/em&gt; call.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Oh&lt;/em&gt;, that recent change? It was &lt;a href="https://nodejs.org/api/fs.html#fs_fs_mkdir_path_options_callback"&gt;recursive &lt;code&gt;fs.mkdir&lt;/code&gt;&lt;/a&gt;.  I’ll explain what makes it different.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shell Operations vs. System Operations
&lt;/h2&gt;

&lt;p&gt;Developers may not think about this much because it’s so well-abstracted by our tools.  Take &lt;code&gt;mkdir&lt;/code&gt;, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; ./foo
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mkdir&lt;/code&gt; is a command-line utility (which flavor, exactly, depends on your operating system).  It’s &lt;em&gt;not&lt;/em&gt; a system call.  The above command may only &lt;em&gt;execute&lt;/em&gt; a single system call, but the following may execute several:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# creates dirs foo, then bar, then baz, ignoring dirs that already exist&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; ./foo/bar/baz
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Unless our tools have &lt;em&gt;transactional&lt;/em&gt; behavior—they can “commit” or “roll back” operations—it’s possible for this command to &lt;em&gt;partially&lt;/em&gt; succeed (though maybe not obvious in this case, but trust me).&lt;/p&gt;

&lt;p&gt;What happens if &lt;code&gt;mkdir -p&lt;/code&gt; fails halfway through?  &lt;em&gt;It depends.&lt;/em&gt; You get zero or more new directories.  Yikes!&lt;/p&gt;

&lt;p&gt;If that seems weird, consider that the user may &lt;em&gt;want&lt;/em&gt; to keep the directories it &lt;em&gt;did&lt;/em&gt; create.  It’s tough to make assumptions about this sort of thing; cleanup is best left to the user, who can deal with the result as they see fit.&lt;/p&gt;

&lt;p&gt;How does this relate to Node.js?  When a developer supplies the &lt;code&gt;recursive: true&lt;/code&gt; option to &lt;code&gt;fs.mkdir&lt;/code&gt;, Node.js will potentially ask &lt;strong&gt;libuv&lt;/strong&gt; to make &lt;em&gt;several&lt;/em&gt; system calls—&lt;em&gt;all, some, or none&lt;/em&gt; of which may succeed.&lt;/p&gt;

&lt;p&gt;Previous to the addition of recursive &lt;code&gt;fs.mkdir&lt;/code&gt;, Node.js had no precedent for this behavior.  Still, its implementation is relatively straightforward; when creating directories, the operations must happen both &lt;em&gt;in order&lt;/em&gt; and  &lt;em&gt;sequentially&lt;/em&gt;—we can’t create &lt;code&gt;bar/baz/&lt;/code&gt; before we create &lt;code&gt;bar/&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;It may be surprising, then, that a recursive &lt;code&gt;rmdir&lt;/code&gt; implementation is another beast entirely.&lt;/p&gt;

&lt;h2&gt;
  
  
  There Was An Attempt
&lt;/h2&gt;

&lt;p&gt;I was likely not the first to attempt to implement a recursive &lt;code&gt;rmdir&lt;/code&gt; in Node.js at the C++ level, but I &lt;em&gt;did&lt;/em&gt; try, and I’ll explain why it didn’t work.&lt;/p&gt;

&lt;p&gt;The idea was that a C++ implementation could be more performant than a JavaScript implementation—that’s probably true!&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;mkdir&lt;/code&gt; as a template, I began coding.  My algorithm would perform a depth-first traversal of the directory tree using &lt;strong&gt;libuv&lt;/strong&gt; ’s &lt;code&gt;uv_fs_readdir&lt;/code&gt;; when it found no more directories to descend into, it would call &lt;code&gt;uv_fs_unlink&lt;/code&gt; on each file therein. Once the directory was clear of files, it would ascend to the parent, and finally remove the now-empty directory.&lt;/p&gt;

&lt;p&gt;It worked!  I was very proud of myself.  Then I decided to run some benchmarks against &lt;a href="https://npm.im/rimraf"&gt;&lt;strong&gt;rimraf&lt;/strong&gt;&lt;/a&gt;. Maybe I shouldn't have!&lt;/p&gt;

&lt;p&gt;I found out that my implementation was faster for a very small &lt;em&gt;N&lt;/em&gt;, where &lt;em&gt;N&lt;/em&gt; is the number of files and directories to remove.  But &lt;em&gt;N&lt;/em&gt; didn’t have to grow very large for userland's &lt;strong&gt;rimraf&lt;/strong&gt; to overtake my implementation.&lt;/p&gt;

&lt;p&gt;Why was mine slower?  Besides using an unoptimized algorithm, I used recursive &lt;code&gt;mkdir&lt;/code&gt; as a template, and &lt;code&gt;mkdir&lt;/code&gt; works &lt;em&gt;in serial&lt;/em&gt; (as I mentioned above).  So, my algorithm only removed &lt;em&gt;one file&lt;/em&gt; at a time.   &lt;strong&gt;rimraf&lt;/strong&gt; , on the other hand, queued up many calls to &lt;code&gt;fs.unlink&lt;/code&gt; and &lt;code&gt;fs.rmdir&lt;/code&gt;.  Because &lt;strong&gt;libuv&lt;/strong&gt; has a thread pool for filesystem operations, it could speedily blast a directory full of files, only limited by its number of threads!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that when we say Node.js is single-threaded, we're talking about the &lt;em&gt;programming model&lt;/em&gt;.  Under the hood, I/O operations are multithreaded.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;At this point, I realized that if it was going to be “worth it” to implement at the C++ layer—meaning a significant performance advantage which outweighs-the- maintenance-costs-of-more-C++-code—I’d have to rewrite the implementation to manage its &lt;em&gt;own&lt;/em&gt; thread pool.  Of course, there’s no great precedent for &lt;em&gt;that&lt;/em&gt; in Node.js either.  It’d be possible, but very tricky, and best left to somebody with a better handle on C++ and multithreaded programming.&lt;/p&gt;

&lt;p&gt;I went back to the &lt;a href="https://github.com/nodejs/tooling"&gt;Node.js tooling group&lt;/a&gt; and explained the situation.  We decided that the most feasible way forward would be a pure-JavaScript implementation of recursive directory removal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let’s Write It In JavaScript!
&lt;/h2&gt;

&lt;p&gt;Well, that was the idea, but we didn’t get very far.  We took a look at the &lt;a href="https://github.com/isaacs/rimraf/blob/master/rimraf.js"&gt;source of &lt;strong&gt;rimraf&lt;/strong&gt;&lt;/a&gt;, which is the most popular userland implementation. It’s not as straightforward as you’d expect!  It covers many edge cases and peculiarities (and all of those hacks would need to be present in a Node.js core implementation; it needs to work like a consumer would expect).&lt;/p&gt;

&lt;p&gt;Furthermore, &lt;strong&gt;rimraf&lt;/strong&gt; is stable, and these workarounds have proven themselves to be robust over the years that it’s been consumed by the ecosystem.&lt;/p&gt;

&lt;p&gt;I won’t attempt to explain what &lt;strong&gt;rimraf&lt;/strong&gt; must do to achieve decent performance in a portable manner—but rest assured it’s sufficiently &lt;em&gt;non-trivial&lt;/em&gt;.  &lt;em&gt;So&lt;/em&gt; non-trivial, in fact, that it made more sense to just &lt;em&gt;pull &lt;strong&gt;rimraf&lt;/strong&gt; into Node.js core&lt;/em&gt; instead of trying to code it again from scratch.&lt;/p&gt;

&lt;p&gt;So that’s what we did.&lt;/p&gt;

&lt;h2&gt;
  
  
  It’s Just rimraf
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/iansu"&gt;Ian Sutherland&lt;/a&gt; extracted the needed code from &lt;strong&gt;rimraf&lt;/strong&gt;.  In particular, &lt;strong&gt;rimraf&lt;/strong&gt; supplies a command-line interface, and we didn’t need that.  For simplicity (and to eliminate dependencies) glob support (e.g., &lt;code&gt;foo/**/*.js&lt;/code&gt;) was also dropped (though it &lt;a href="https://github.com/nodejs/tooling/issues/38"&gt;may still have a future&lt;/a&gt;).  After this, it was a matter of integrating it into a Node.js-style API, and the needed docs and tests.&lt;/p&gt;

&lt;p&gt;To be clear, recursive directory removal in Node.js does &lt;em&gt;not&lt;/em&gt; make rimraf obsolete. It &lt;em&gt;does&lt;/em&gt; mean that for many use cases, Node.js’ &lt;code&gt;fs.rmdir&lt;/code&gt; can get the job done.  Stick with &lt;strong&gt;rimraf&lt;/strong&gt; if you need globs or a portable command-line utility.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://github.com/isaacs/"&gt;Isaac Schlueter&lt;/a&gt; for &lt;strong&gt;rimraf&lt;/strong&gt; —and to bless Node.js’ copy-and-paste efforts.&lt;/p&gt;

&lt;h2&gt;
  
  
  In Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s the story of Node.js’ recursive &lt;code&gt;rmdir&lt;/code&gt; thus far. Want to help write the rest? Come participate in the &lt;a href="https://github.com/nodejs/tooling"&gt;Node.js Tooling Group&lt;/a&gt;, where we’re looking to make Node.js &lt;em&gt;the best platform it can be&lt;/em&gt; for building CLI apps.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Acknowledgements to Isaac Schlueter and Ian Sutherland for reviewing this post.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This post originally appeared on &lt;a href="https://boneskull.com/recursive-directory-removal-in-node-js/"&gt;boneskull.com&lt;/a&gt; on Sep 9, 2019.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>PRO TIPS for devs working at home</title>
      <dc:creator>Christopher Hiller</dc:creator>
      <pubDate>Tue, 11 Jun 2019 19:13:17 +0000</pubDate>
      <link>https://forem.com/boneskull/pro-tips-for-devs-working-at-home-3b63</link>
      <guid>https://forem.com/boneskull/pro-tips-for-devs-working-at-home-3b63</guid>
      <description>&lt;p&gt;Having spent the better part of the last decade as a work-from-home developer, I have discovered or adopted a few LIFE HACKS which I am going to share with you now.  In no way am I claiming that these WEIRD TRICKS will work for everyone; however, they work for me.&lt;/p&gt;

&lt;p&gt;I want to also be clear: unless I'm traveling, I work &lt;em&gt;almost exclusively&lt;/em&gt; out of my home office, and this LISTICLE is written from such a perspective. Here goes...&lt;/p&gt;

&lt;h2&gt;
  
  
  1. You need a home office
&lt;/h2&gt;

&lt;p&gt;Look, we both know you can code while sitting in front of the TV &lt;em&gt;just fine.&lt;/em&gt;  But if you're looking to reach new, towering heights of productivity, you're going to want a home office. Because:&lt;/p&gt;

&lt;h3&gt;
  
  
  Health
&lt;/h3&gt;

&lt;p&gt;Despite being called a "laptop", your lap makes for profoundly un-ergonomic computing.  Your lap, the couch, the bed, the floor—you can work in these places for short sprints.  But it's hard on your eyes, your wrists, your back, your neck, your spleen, and &lt;em&gt;you could catch a cold that way.&lt;/em&gt;  &lt;/p&gt;

&lt;h3&gt;
  
  
  Psychology
&lt;/h3&gt;

&lt;p&gt;There's some sort of psychological THING that makes it so you're more likely to focus on work when you are in a &lt;em&gt;working space&lt;/em&gt;.  No, I'm not a psychologist.  But I know that when I sit behind the wheel of a car, I am going to be focused on driving.&lt;/p&gt;

&lt;h3&gt;
  
  
  Control
&lt;/h3&gt;

&lt;p&gt;You wield total control over your environment.  Your office will have a door which you can—and often do—keep shut.  You will avoid interruption by your terrible family, inconsiderate roommates, or bumbling henchmen.  If you want the lights on, turn them on.  If you want a window open, open it.  &lt;/p&gt;

&lt;h2&gt;
  
  
  2. Professionals need professional tools
&lt;/h2&gt;

&lt;p&gt;Contractors don't build houses with cut-rate power tools.  You shouldn't skimp on your equipment either.&lt;/p&gt;

&lt;h3&gt;
  
  
  Meetings
&lt;/h3&gt;

&lt;p&gt;Unfortunately, you will be videoconferencing. You need some headphones, a decent microphone, and—often neglected—an OS that "just works" with these and your videoconferencing app of choice. A "decent microphone" is subjective, but expect to pay around $50 USD minimum.  You will also want a boom (the extendable "arm" thing) so you can position the microphone correctly; don't just set it on the desk a couple feet from your face! The headphones can be crap, but using them will mean that any echoing or feedback on your conference calls &lt;em&gt;won't be your fault&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Finally, if possible, use a gigabit ethernet wired connection to reduce latency on calls.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ergonomics
&lt;/h3&gt;

&lt;p&gt;Get an adjustable sit-stand desk or converter. Get an external keyboard and trackpad/mouse with wrist rests.  Use an external monitor &amp;amp; laptop stand.  Ergonomics!!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. In the morning, BEGIN; when you get to the end, STOP
&lt;/h2&gt;

&lt;p&gt;The biggest complaint I hear from those unaccustomed to working from home is the struggle to separate &lt;em&gt;work&lt;/em&gt; from &lt;em&gt;home&lt;/em&gt;.  A home office &lt;em&gt;absolutely&lt;/em&gt; helps, but it's only part of a solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  BEGIN-ing
&lt;/h3&gt;

&lt;p&gt;You should have some sort of schedule.  While you may have extra flexibility with &lt;em&gt;when&lt;/em&gt; you work, pick some "core hours."  Work with your teammates/manager to find what works best, and announce when you will be available.&lt;/p&gt;

&lt;h3&gt;
  
  
  STOP-ing
&lt;/h3&gt;

&lt;p&gt;At the end of your workday, physically shut your laptop.  Unplug it and put it away and don't open it until tomorrow.  If you want to work on personal projects or use a computer for games or movies or whatever, &lt;em&gt;use a personal computer,&lt;/em&gt; not the one your employer gave you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Separation
&lt;/h3&gt;

&lt;p&gt;Yes! You should have your own computer.  Not only can it help with managing work/life balance, it works the &lt;em&gt;other&lt;/em&gt; way, by keeping your work machine free of personal data and distractions.  Also, "your own computer" becomes crucial if you happen to be "between jobs."&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Some other good habits
&lt;/h2&gt;

&lt;p&gt;Because I'm starting to sound like a self-help guru here, let me add: &lt;em&gt;do what you can&lt;/em&gt;. These are &lt;strong&gt;not rules&lt;/strong&gt;, and you should not feel bad if they don't stick or if they don't work for you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get dressed, but comfort should be top priority.  Ideally, this is something you wouldn't be embarrassed to leave the house in, but feel free to reassess what you consider to be shameful.&lt;/li&gt;
&lt;li&gt;Often, those unaccustomed to working from home will feel there are too many distractions.  &lt;em&gt;Personally&lt;/em&gt;, I don't feel I have a problem with this, but if I had to pick the biggest distraction, it'd be &lt;em&gt;the bed.&lt;/em&gt;  The bed is in your house.  And it's comfy.  If you fall prey to its siren call, it should be with &lt;em&gt;intent&lt;/em&gt; (e.g., a short, planned "power nap", complete with alarm).  To discourage casual use, &lt;em&gt;make the bed&lt;/em&gt; before beginning work for the day.&lt;/li&gt;
&lt;li&gt;Get up and move around often, even for just a few minutes.  Walk out and get the mail, take the dog to pee, water some plants, etc.&lt;/li&gt;
&lt;li&gt;Don't eat in your office.  Eat in the kitchen.  In other words, don't work through lunch.  When you sit back down to revisit whatever it was you were hacking on, this can give you a fresh perspective.&lt;/li&gt;
&lt;li&gt;You (probably) aren't wearing an ankle monitor.  So if you feel your social needs aren't being met, for crying out loud, leave the house!  Go work in a coffee shop or shared workspace or something.  Schedule meetups with other remote teammates.  If there's an actual office nearby, give it a visit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. There was an attempt
&lt;/h2&gt;

&lt;p&gt;If you currently work in an office and have the option to work from home, I'd strongly suggest easing in to it.  Start with a day or two a week, try some of the above suggestions, and go from there.&lt;/p&gt;

&lt;p&gt;Despite having given it time and effort, you might still feel unproductive or just downright lonely.  This is not unheard of!  Working from home—or even just working "remotely"—is not for everyone.  Reaching this conclusion doesn't mean you've failed; it means you've learned about yourself and what you need from a job.&lt;/p&gt;




&lt;p&gt;I hope these ideas are helpful!  Could you at least put some pants on, though? &lt;/p&gt;

&lt;p&gt;Oh, and if you think these tips are &lt;em&gt;not awful&lt;/em&gt;, I also spoke them with my mouth on &lt;a href="https://changelog.com/jsparty/76"&gt;JS Party #76&lt;/a&gt; alongside &lt;a href="https://twitter.com/EmmaWedekind"&gt;Emma Wedekind&lt;/a&gt; and &lt;a href="https://twitter.com/kbal1"&gt;KBall&lt;/a&gt;. Take a listen.&lt;/p&gt;


&lt;div class="podcastliquidtag"&gt;
  &lt;div class="podcastliquidtag__info"&gt;
    &lt;a href="/jsparty/you-don-t-have-to-dress-up"&gt;
      &lt;h1 class="podcastliquidtag__info__episodetitle"&gt;You don’t have to dress up&lt;/h1&gt;
    &lt;/a&gt;
    &lt;a href="/jsparty"&gt;
      &lt;h2 class="podcastliquidtag__info__podcasttitle"&gt;
        JS Party  

      &lt;/h2&gt;
    &lt;/a&gt;
  &lt;/div&gt;
  &lt;div id="record-you-don-t-have-to-dress-up" class="podcastliquidtag__record"&gt;
    &lt;img class="button play-butt" id="play-butt-you-don-t-have-to-dress-up" src="/assets/playbutt.png" alt="play"&gt;
    &lt;img class="button pause-butt" id="pause-butt-you-don-t-have-to-dress-up" src="/assets/pausebutt.png" alt="pause"&gt;
    &lt;img class="podcastliquidtag__podcastimage" id="podcastimage-you-don-t-have-to-dress-up" alt="JS Party" src="https://res.cloudinary.com/practicaldev/image/fetch/s--OKzBBRKS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--9X5wejAR--/c_fill%2Cf_auto%2Cfl_progressive%2Cq_auto/https://dev-to-uploads.s3.amazonaws.com/uploads/podcast/image/46/ce547895-87b7-4443-a752-3ea70febb311.svg"&gt;
  &lt;/div&gt;
  &lt;div class="hidden-audio" id="hidden-audio-you-don-t-have-to-dress-up"&gt;
    
      
      Your browser does not support the audio element.
    
    &lt;div id="progressBar" class="audio-player-display"&gt;
      &lt;a href="/jsparty/you-don-t-have-to-dress-up"&gt;
        &lt;img width="420" height="420" id="episode-profile-image" alt="You don’t have to dress up" src="https://res.cloudinary.com/practicaldev/image/fetch/s--JwUSgiAO--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--G9Yn3fky--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_420%2Cq_auto%2Cw_420/https://dev-to-uploads.s3.amazonaws.com/uploads/podcast/image/46/ce547895-87b7-4443-a752-3ea70febb311.svg"&gt;
        &lt;img id="animated-bars" src="/assets/animated-bars.gif" alt="animated volume bars"&gt;
      &lt;/a&gt;
      &lt;span id="barPlayPause"&gt;
        &lt;img class="butt play-butt" src="/assets/playbutt.png" alt="play"&gt;
        &lt;img class="butt pause-butt" src="/assets/pausebutt.png" alt="pause"&gt;
      &lt;/span&gt;
      &lt;span id="volume"&gt;
        &lt;span id="volumeindicator" class="volume-icon-wrapper showing"&gt;
          &lt;span id="volbutt"&gt;
            &lt;img alt="volume" class="icon-img" height="16" width="16" src="https://res.cloudinary.com/practicaldev/image/fetch/s--SnhE4kcy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/volume.png"&gt;
          &lt;/span&gt;
          &lt;span class="range-wrapper"&gt;
            
          &lt;/span&gt;
        &lt;/span&gt;
        &lt;span id="mutebutt" class="volume-icon-wrapper hidden"&gt;
          &lt;img alt="mute" class="icon-img" height="16" width="16" src="https://res.cloudinary.com/practicaldev/image/fetch/s--prPRZNLS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/volume-mute.png"&gt;
        &lt;/span&gt;
        &lt;span class="speed" id="speed"&gt;1x&lt;/span&gt;
      &lt;/span&gt;
      &lt;span class="buffer-wrapper" id="bufferwrapper"&gt;
        &lt;span id="buffer"&gt;&lt;/span&gt;
        &lt;span id="progress"&gt;&lt;/span&gt;
        &lt;span id="time"&gt;initializing...&lt;/span&gt;
        &lt;span id="closebutt"&gt;×&lt;/span&gt;
      &lt;/span&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>productivity</category>
      <category>career</category>
      <category>devtips</category>
    </item>
  </channel>
</rss>
