<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Devin Witherspoon</title>
    <description>The latest articles on DEV Community by Devin Witherspoon (@dcwither).</description>
    <link>https://dev.to/dcwither</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%2F483011%2Ff60ac531-6dad-454f-8c22-c66a3360b4ee.jpeg</url>
      <title>DEV Community: Devin Witherspoon</title>
      <link>https://dev.to/dcwither</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dcwither"/>
    <language>en</language>
    <item>
      <title>Demystifying NPM Scripts</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 17 Jan 2021 13:18:05 +0000</pubDate>
      <link>https://dev.to/dcwither/demystifying-npm-scripts-4cb4</link>
      <guid>https://dev.to/dcwither/demystifying-npm-scripts-4cb4</guid>
      <description>&lt;p&gt;If you're new to Node or have only worked on projects that already have their npm scripts set up, then you may be wondering what npm &lt;code&gt;scripts&lt;/code&gt; are and how they work. In this article, I'll give my best intuitive explanation for how/why they work and highlight some of the key tools available for writing simple npm scripts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are These Scripts?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;package.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://docs.npmjs.com/cli/v6/configuring-npm/package-json"&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/a&gt; is the npm configuration file for a project, including its dependencies, project details, and &lt;code&gt;scripts&lt;/code&gt;. &lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-run-script"&gt;&lt;code&gt;npm run&lt;/code&gt;&lt;/a&gt; is the npm command that executes commands written in the &lt;code&gt;scripts&lt;/code&gt; section.&lt;/p&gt;

&lt;p&gt;For my &lt;a href="https://github.com/dcwither/articles-template/blob/main/package.json"&gt;template article repo&lt;/a&gt;, I set up a linter script &lt;code&gt;lint&lt;/code&gt;, which is run with &lt;code&gt;npm run lint&lt;/code&gt;. Running &lt;code&gt;prettier **/*.md&lt;/code&gt; in the terminal directly wouldn't work because &lt;code&gt;prettier&lt;/code&gt; isn't found in the global &lt;code&gt;PATH&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier **/*.md"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&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;"prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.1.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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Meet &lt;code&gt;PATH&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PATH&lt;/code&gt; is an environment variable that lists directories where the shell should look for commands. You can find a more thorough explanation on &lt;a href="https://linuxhint.com/path_in_bash/"&gt;Linux Hint&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In order to find out what's inside your &lt;code&gt;PATH&lt;/code&gt; you can run &lt;code&gt;echo $PATH&lt;/code&gt;. Running that on a &lt;a href="https://repl.it/@dcwither/npm-scripting-tutorial"&gt;&lt;code&gt;repl.it&lt;/code&gt; sandbox&lt;/a&gt;, I got the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/npm-scripting-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PATH&lt;/span&gt;
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At least for our purposes, all &lt;code&gt;npm run&lt;/code&gt; does is append a few more directories onto the &lt;code&gt;PATH&lt;/code&gt;. We can confirm this by making the following npm script, and running it ourselves:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"echo $PATH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier **/*.md"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;~/npm-scripting-tutorial&lt;span class="nv"&gt;$ &lt;/span&gt;npm run path

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; @ path /home/runner/npm-scripting-tutorial
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$PATH&lt;/span&gt;

/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/home/runner/npm-scripting-tutorial/node_modules/.bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So &lt;code&gt;npm run&lt;/code&gt; prepended the following section to the &lt;code&gt;PATH&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/home/runner/npm-scripting-tutorial/node_modules/.bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;/home/runner/npm-scripting-tutorial/node_modules/.bin&lt;/code&gt; section is what gives us access to &lt;code&gt;prettier&lt;/code&gt; in the &lt;code&gt;npm lint&lt;/code&gt; command.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Does This Mean?
&lt;/h3&gt;

&lt;p&gt;The expansion of the &lt;code&gt;PATH&lt;/code&gt; is what allows us to call commands written in other npm packages without referencing their exact location in our &lt;code&gt;node_modules&lt;/code&gt; folder. If we didn't have this, our scripts would look more like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./node_modules/.bin/prettier **/*.md"&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;That wouldn't be terrible, but it's not exactly ideal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Highlights
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Aliases
&lt;/h3&gt;

&lt;p&gt;Some commands are so common that npm aliases them so they don't need to be prefixed by &lt;code&gt;run&lt;/code&gt;. These include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-build"&gt;&lt;code&gt;npm build&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-start"&gt;&lt;code&gt;npm start&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-stop"&gt;&lt;code&gt;npm stop&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.npmjs.com/cli/v6/commands/npm-test"&gt;&lt;code&gt;npm test&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So running &lt;code&gt;npm start&lt;/code&gt; is the same as running &lt;code&gt;npm run start&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lifecycle Operations
&lt;/h3&gt;

&lt;p&gt;There are some names that are hooks into steps of npm lifecycle commands (e.g. &lt;code&gt;npm publish&lt;/code&gt;, &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;npm start&lt;/code&gt;). You can add scripts with these names to trigger commands at those steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc --project ."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prepack"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run build"&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;One unintuitive quirk with lifecycle scripts is that &lt;a href="https://docs.npmjs.com/cli/v6/using-npm/scripts#prepare-and-prepublish"&gt;&lt;code&gt;prepare&lt;/code&gt; and &lt;code&gt;prepublish&lt;/code&gt;&lt;/a&gt; (both of which are now deprecated) also trigger on local &lt;code&gt;npm install&lt;/code&gt;, so if you have a build step that shouldn't trigger on install, it would be better associated with &lt;code&gt;prepublishOnly&lt;/code&gt; or &lt;code&gt;prepack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The docs include more info on other &lt;a href="https://docs.npmjs.com/cli/v6/using-npm/scripts#life-cycle-operation-order"&gt;lifecycle operations&lt;/a&gt; that you can hook into.&lt;/p&gt;

&lt;h3&gt;
  
  
  Command Arguments
&lt;/h3&gt;

&lt;p&gt;Normally if we pass in a &lt;code&gt;--option&lt;/code&gt; to &lt;code&gt;npm run&lt;/code&gt;, it won't pass it through to the command written in &lt;code&gt;scripts&lt;/code&gt;. For instance, to make &lt;code&gt;prettier&lt;/code&gt; automatically fix issues, we would want to pass the &lt;code&gt;--write&lt;/code&gt; option. If we add a &lt;code&gt;--&lt;/code&gt; before the options, they will be passed through. This means we update our &lt;code&gt;npm run lint&lt;/code&gt; command above to the following to execute &lt;code&gt;prettier --write&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 run lint &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--write&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Hopefully this quick intro to the concepts around npm scripts makes it easier to read the scripts you encounter, as well as start writing your own. If you have any other questions, I recommend you look through npm's well-written documentation, starting with the &lt;a href="https://docs.npmjs.com/cli/v6"&gt;npm CLI docs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>npm</category>
      <category>node</category>
      <category>bash</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Surviving Performance Review Season</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Mon, 11 Jan 2021 04:03:10 +0000</pubDate>
      <link>https://dev.to/dcwither/surviving-performance-review-season-c1b</link>
      <guid>https://dev.to/dcwither/surviving-performance-review-season-c1b</guid>
      <description>&lt;p&gt;'Tis the season where we all pick up our pens and start writing performance reviews for ourselves, our managers, and our peers. Even if you've prepared ahead of time, this can be a stressful time of year where you have to look back on your recent work and find those nuggets that best represent what you've done over the last six months to a year.&lt;/p&gt;

&lt;p&gt;Only you know everything you've done. Your manager and others evaluating your performance don't. Writing a strong self review empowers your manager to advocate for you. So how do we do that? This isn't a definitive guide on writing a performance review, rather a reminder to myself of tools I've used to make this time more manageable and sometimes even enjoyable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documenting
&lt;/h2&gt;

&lt;p&gt;Looking over six months of work can be really hard if you haven't been documenting what you've been doing so far. I use a Trello board with lists representing quarters and cards representing each week. These cards include a highlight of the week in the title, as well as checklists of what I did and planned to do throughout the week.&lt;/p&gt;

&lt;p&gt;For larger projects that I want to highlight, I have a separate template that I add to the bottom of each list to ensure I include them in my self review. The templates make authoring the weekly cards and projects easier and more consistent.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NytYE2cB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dzwc80d8jq1fdpkqduhs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NytYE2cB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dzwc80d8jq1fdpkqduhs.png" alt="Template Quarterly Accomplishment trello board"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Here's a &lt;a href="https://trello.com/b/PC3G3DN1/template-quarterly-accomplishments"&gt;template version&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Self Review
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Gathering Information
&lt;/h3&gt;

&lt;p&gt;In order to gather additional information, as well as make up for any gaps in my documentation, I use the following sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull requests I've authored and reviewed&lt;/li&gt;
&lt;li&gt;Tickets I've owned&lt;/li&gt;
&lt;li&gt;Threads from conversations with teammates&lt;/li&gt;
&lt;li&gt;Incident channels I participated in&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Writing The Review
&lt;/h3&gt;

&lt;p&gt;Most of the self review prompts I've encountered are broken into three broad sections.&lt;/p&gt;

&lt;h4&gt;
  
  
  What Did You Do well?
&lt;/h4&gt;

&lt;p&gt;A laundry list of tasks isn't enough - this is where I tell a narrative of the things I've done and their impact. Going beyond technical contributions, I include &lt;a href="https://noidea.dog/glue"&gt;glue work&lt;/a&gt;, recruiting, and mentoring, tying them back to the organization's values. These other contributions are rarely as valued by leadership as technical ones, but they should be equally valued, if not more so, and round out your profile/narrative.&lt;/p&gt;

&lt;h4&gt;
  
  
  What Can You Improve On?
&lt;/h4&gt;

&lt;p&gt;I try to be as honest as possible here, focusing on things that made my job in the last few months harder. In the past this has included communicating more clearly and proactively, working more effectively with people outside my area of expertise, and using my influence to make the recruitment process more equitable and consistent. Ideally with each self review, I can reference previous areas of improvement as places where I've made progress.&lt;/p&gt;

&lt;h4&gt;
  
  
  Rate Yourself
&lt;/h4&gt;

&lt;p&gt;Often performance reviews ask you to grade your own performance. By default I start from a position of "Exceeds", and then review my contributions to convince myself whether it should be higher or lower.&lt;/p&gt;

&lt;p&gt;Leveling yourself as "Meets all"/"Meets most" whether due to lack of confidence or humility often leads to managers accepting that rating and not pushing for a higher rating. If they disagree with the "Exceeds" rating, then this is a great opportunity to learn how their expectations of an "Exceeds" rating differ from your understanding of your own performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ask a Peer to Review It
&lt;/h3&gt;

&lt;p&gt;Having someone on your team, likely one of your peer reviewers, look over your self review (and doing the same for them) can help you catch things you missed and identify sections that need more detail/context to better describe the impact you had.&lt;/p&gt;

&lt;h2&gt;
  
  
  Peer Reviews
&lt;/h2&gt;

&lt;p&gt;Peer review prompts are similar to those in self reviews but normally replace the rating with a section for additional notes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gathering Information
&lt;/h3&gt;

&lt;p&gt;For peer reviews, I often sync with teammates to get an idea of the story they're trying to tell about their performance and to refresh myself on what we've worked on together. This gives them an opportunity to highlight what they think I would have unique context on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing the Review
&lt;/h3&gt;

&lt;h3&gt;
  
  
  What did your peer do well?
&lt;/h3&gt;

&lt;p&gt;Similar to in my self review, I try to highlight what they did as an individual, identifying specific contributions and the impact they had. I'm usually less detailed for this than my self review since I'm writing three to six of these. Highlighting their contributions that I have a unique perspective on is the priority here. This is often my favorite part of performance review season because I get to celebrate the achievements of others.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Could Your Peer Improve on?
&lt;/h3&gt;

&lt;p&gt;What I highlight depends on my teammate's experience level and where they're interested in growing. The more senior the teammate, the more I focus on their leadership, glue work, and how they drive change in the organization.&lt;/p&gt;

&lt;p&gt;I've also had teammates that are performing well beyond their current level and I genuinely can't think of reasonable areas of improvement. In this scenario, I make recommendations for what they can do that's beyond the scope of their current role (e.g. recommend that they tech lead a project in the next cycle).&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Notes
&lt;/h3&gt;

&lt;p&gt;This is for anything that doesn't fit in the previous two categories. In the past I've used this section for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Describing mitigating factors for areas of improvement.&lt;/li&gt;
&lt;li&gt;Giving a recommendation for promotion.&lt;/li&gt;
&lt;li&gt;Giving additional context for the feedback.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;So are performance reviews fun? Generally no, but doing them methodically can make them less intimidating and more effective. Making sure you find the process that works for you and allows your manager to effectively advocate for you is the most important piece here. An added benefit is the work I put into my self reviews becomes the foundation for my resume.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tutorial</category>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>Everything's More Complex Than it First Appears</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Mon, 04 Jan 2021 00:58:18 +0000</pubDate>
      <link>https://dev.to/dcwither/everything-s-more-complex-than-it-first-appears-33of</link>
      <guid>https://dev.to/dcwither/everything-s-more-complex-than-it-first-appears-33of</guid>
      <description>&lt;p&gt;Coding in SCSS, like in any other programming language, should always have a goal of optimizing for readability over writing speed. Unfortunately some of the syntax available in SCSS can make it harder to read/understand. An example of this is the &lt;a href="https://sass-lang.com/documentation/style-rules/parent-selector" rel="noopener noreferrer"&gt;parent selector&lt;/a&gt; (&lt;code&gt;&amp;amp;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;The parent selector is handy for pseudo-classes (e.g. &lt;code&gt;&amp;amp;:hover&lt;/code&gt;) and using the context in a flexible manner (e.g. &lt;code&gt;:not(&amp;amp;)&lt;/code&gt;), though we can also abuse this to create "union class names".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.parent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;-extension&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;This usage poses some issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can't search for the resulting CSS class used by your HTML (&lt;code&gt;parent-extension&lt;/code&gt;) within the codebase.&lt;/li&gt;
&lt;li&gt;If you use this pattern in a larger file, you may need to look through multiple levels of nesting to mentally calculate the resulting CSS class.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article follows the ongoing process of creating the &lt;code&gt;union-class-name&lt;/code&gt; command of &lt;a href="https://github.com/dcwither/scss-codemods" rel="noopener noreferrer"&gt;dcwither/scss-codemods&lt;/a&gt;, with the goal of eliminating our codebase's approximately 2,000 instances of the union class pattern.&lt;/p&gt;
&lt;h2&gt;
  
  
  Future Proofing
&lt;/h2&gt;

&lt;p&gt;To limit the spread of the existing pattern, I introduced the &lt;a href="https://github.com/kristerkari/stylelint-scss/blob/master/src/rules/selector-no-union-class-name/README.md" rel="noopener noreferrer"&gt;&lt;code&gt;selector-no-union-class-name&lt;/code&gt; Stylelint SCSS Rule&lt;/a&gt; to the project. Unfortunately this didn't fix the existing 2,000 instances of this pattern throughout our codebase. In order to make a wider fix, I turned to PostCSS.&lt;/p&gt;
&lt;h2&gt;
  
  
  PostCSS to the Rescue!
&lt;/h2&gt;

&lt;p&gt;The idea I had was to write a PostCSS script to "promote" nested rules that begin with &lt;code&gt;&amp;amp;-&lt;/code&gt; to their parent context after their parent.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: This Should be Easy, Right?
&lt;/h3&gt;

&lt;p&gt;Using &lt;a href="https://astexplorer.net/" rel="noopener noreferrer"&gt;AST Explorer&lt;/a&gt; as an experimentation tool, I played with transforms until I found something that looked like it worked:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remove-nesting-selector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&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;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walkRules&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substr&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="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://astexplorer.net/#/gist/d69726c09b4d83e5cbee898ebbe32119/979c549f331c689ae46a4f4f08b7b8ebaac73be9" rel="noopener noreferrer"&gt;Attempt 1 AST Explorer snippet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first problem I noticed was that the script was reversing the classes it promoted. This can change the precedence in which conflicting CSS rules are applied, resulting in a change in behavior.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.some-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;-part1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;-part2&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="c1"&gt;// becomes&lt;/span&gt;

&lt;span class="nc"&gt;.some-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.some-class-part2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.some-class-part1&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;This may not be a problem if those classes aren't used by the same elements, but without the relevant HTML, we have no way of knowing whether that's the case.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Okay, Let's Fix That One Bug
&lt;/h3&gt;

&lt;p&gt;So all we need to do is maintain the promoted class orders, right?&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plugin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;remove-nesting-selector&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;lastParent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;insertAfterTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;walkRules&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;-&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ruleParent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ruleParent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;substr&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastParent&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;ruleParent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;insertAfterTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lastParent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ruleParent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;ruleParent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertAfter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;insertAfterTarget&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;insertAfterTarget&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rule&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://astexplorer.net/#/gist/d69726c09b4d83e5cbee898ebbe32119/2331059f49aab0ac7d32f31b6707db36310c9ed6" rel="noopener noreferrer"&gt;Attempt 2 AST Explorer snippet&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now promoted classes maintain their order, but the transformed SCSS fails to build because of SCSS variables that don't exist where they're referenced.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.some-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nt"&gt;-part1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$color&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="c1"&gt;// becomes&lt;/span&gt;

&lt;span class="nc"&gt;.some-class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;$color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.some-class-part1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$color&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;This is where I started to realize the complexity of this problem. Variables can reference other variables, so we need to deal with that recursion. What about name collisions? What if I break something that was already working in an attempt to fix something else?&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 3: Time For Some Structure
&lt;/h3&gt;

&lt;p&gt;I wasn't going to finish this project in an afternoon with AST Explorer. At this point I decided to move the project into a GitHub repo so I could manage the increased complexity.&lt;/p&gt;

&lt;p&gt;From here, the development process became much more formal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wrote tests for existing code.&lt;/li&gt;
&lt;li&gt;Wrote test stubs for features I wanted to implement.&lt;/li&gt;
&lt;li&gt;Created a &lt;a href="https://github.com/dcwither/scss-codemods/projects/1" rel="noopener noreferrer"&gt;GitHub project&lt;/a&gt; (Kanban board) to track tasks.&lt;/li&gt;
&lt;li&gt;Started thinking about a CLI that others could use.&lt;/li&gt;
&lt;li&gt;Documented the intended behavior in a README.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though I was the only person working on this, it became necessary to follow these practices as the project grew because I could no longer hold the entire project and behavior in my head.&lt;/p&gt;
&lt;h4&gt;
  
  
  Verifying
&lt;/h4&gt;

&lt;p&gt;Unit tests, while helpful for documenting and verifying assumptions, are insufficient for ensuring the transform won't have any negative impacts on the resulting CSS. By compiling the SCSS before and after the transformation, we can &lt;code&gt;diff&lt;/code&gt; the CSS to confirm there are no changes.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;diff &lt;span class="nt"&gt;--side-by-side&lt;/span&gt; &lt;span class="nt"&gt;--suppress-common-lines&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"/&lt;/span&gt;&lt;span class="se"&gt;\*&lt;/span&gt;&lt;span class="s2"&gt; line"&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;before_tranform_css]&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"/&lt;/span&gt;&lt;span class="se"&gt;\*&lt;/span&gt;&lt;span class="s2"&gt; line"&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;after_transform_css]&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;If you're interested in the more complicated testing I did, you can check out &lt;a href="https://dev.to/dcwither/writing-cleaner-tests-with-jest-extensions-5fmb"&gt;Writing Cleaner Tests with Jest Extensions&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  All the Bugs &lt;strong&gt;So Far&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So what did I realize I had missed along the way?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Multiple nesting selectors in a given selector.&lt;/li&gt;
&lt;li&gt;Scoped variables that need to be promoted along with the promoted rules.&lt;/li&gt;
&lt;li&gt;In &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors#Grouping_selectors" rel="noopener noreferrer"&gt;Grouping Selectors&lt;/a&gt; (&lt;code&gt;.a, .b&lt;/code&gt;), every member must begin with &lt;code&gt;&amp;amp;-&lt;/code&gt; for the rule to be promoted.&lt;/li&gt;
&lt;li&gt;Not accounting for the multiplicative factor of nested grouping selectors (&lt;a href="https://github.com/dcwither/scss-codemods/blob/v0.1.2/src/plugins/remove-dash-ampersand.test.js#L120-L138" rel="noopener noreferrer"&gt;see this test&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Duplicate scoped SCSS variables.&lt;/li&gt;
&lt;li&gt;Promoting a rule &lt;em&gt;may&lt;/em&gt; change the order of the rules in the compiled CSS.&lt;/li&gt;
&lt;li&gt;Promoting SCSS variables to global scope can affect other files.&lt;/li&gt;
&lt;li&gt;SCSS variables can have interdependencies and may require recursive promotions.&lt;/li&gt;
&lt;li&gt;Everything about variables applies to functions and mixins.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  &lt;del&gt;Learnings&lt;/del&gt; Re-Learnings
&lt;/h2&gt;

&lt;p&gt;This project isn't finished, but it has finished its arc of escalating from an afternoon of coding in a web editor to having the necessary infrastructure and testing to continue developing with confidence.&lt;/p&gt;

&lt;p&gt;The general lesson here, which I find myself relearning from time to time, is that the work necessary to fulfill an idea is often much more complex than what you initially imagine. Because I hadn't spent much time with SCSS in a while, variables, mixins, and grouping selectors weren't top of mind. I had a myopic perspective of the language and problem (nesting and parent selector) that made the problem appear much simpler than in reality.&lt;/p&gt;

&lt;p&gt;The bright side is, as I realized the problem needed a more complex solution, I adapted well, gradually increasing the process around the solution. Moving assumptions, requirements, and specifications out of my head and into code/tests/project boards made the entire project more manageable. The other learning is that I no longer assume that this transform is correct - it's only &lt;strong&gt;correct enough&lt;/strong&gt; to be useful in the scenarios I have encountered.&lt;/p&gt;

&lt;p&gt;If you're interested in the project, you can check it out below:&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dcwither" rel="noopener noreferrer"&gt;
        dcwither
      &lt;/a&gt; / &lt;a href="https://github.com/dcwither/scss-codemods" rel="noopener noreferrer"&gt;
        scss-codemods
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      SCSS codemods written with postcss plugins
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;scss-codemods&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;This project uses postcss to refactor scss code to conform to lint rules that are intended to improve grepability/readability.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Installation&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Globally via &lt;code&gt;npm&lt;/code&gt;
&lt;/h3&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm i -g scss-codemods&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Running on-demand&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;npx scss-codemods [command] [options]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;&lt;code&gt;union-class-name&lt;/code&gt;&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;"Promotes" CSS classes that have the &lt;code&gt;&amp;amp;-&lt;/code&gt; nesting union selector. Attempts to fix issues flagged by &lt;a href="https://github.com/kristerkari/stylelint-scss/blob/master/src/rules/selector-no-union-class-name/README.md" rel="noopener noreferrer"&gt;scss/no-union-class-name&lt;/a&gt; stylelint rule.&lt;/p&gt;

&lt;p&gt;e.g.&lt;/p&gt;

&lt;div class="highlight highlight-source-css-scss notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-e"&gt;.rule&lt;/span&gt; {
  &lt;span class="pl-ent"&gt;&amp;amp;&lt;/span&gt;&lt;span class="pl-e"&gt;-suffix&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;color&lt;/span&gt;&lt;/span&gt;: &lt;span class="pl-c1"&gt;blue&lt;/span&gt;;
  }
}

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;//&lt;/span&gt; becomes&lt;/span&gt;

&lt;span class="pl-e"&gt;.rule-suffix&lt;/span&gt; {
  &lt;span class="pl-c1"&gt;&lt;span class="pl-c1"&gt;color&lt;/span&gt;&lt;/span&gt;: &lt;span class="pl-c1"&gt;blue&lt;/span&gt;;
}&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Intended to improve "grepability" of the selectors that are produced in the browser.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Usage&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;scss-codemods union-class-name --reorder never &lt;span class="pl-k"&gt;&amp;lt;&lt;/span&gt;files&lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Options&lt;/h3&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;&lt;code&gt;--reorder&lt;/code&gt;&lt;/h4&gt;

&lt;/div&gt;

&lt;p&gt;Determines the freedom provided to the codemod to reorder rules to better match the desired format (default: &lt;code&gt;never&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Values:&lt;/strong&gt;&lt;/p&gt;


&lt;ul&gt;

&lt;li&gt;

&lt;code&gt;never&lt;/code&gt;: won't promote rules if it would result in the reordering of selectors.&lt;/li&gt;

&lt;li&gt;

&lt;code&gt;safe-only&lt;/code&gt;: will promote rules that result in the reordering of selectors as long as the reordered selectors…&lt;/li&gt;

&lt;/ul&gt;
&lt;/div&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dcwither/scss-codemods" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>css</category>
      <category>codequality</category>
      <category>javascript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Hobbies Make Me a Better Engineer</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 27 Dec 2020 15:05:17 +0000</pubDate>
      <link>https://dev.to/dcwither/hobbies-make-me-a-better-engineer-48a2</link>
      <guid>https://dev.to/dcwither/hobbies-make-me-a-better-engineer-48a2</guid>
      <description>&lt;p&gt;Recently there's been a lot of discussion on Twitter about whether "frontend engineer" is its own specialization. I'm not up for arguing this - my opinion is yes, it absolutely is, and in fact I've worked with people with several specializations within frontend development (accessibility, architecture, performance, infrastructure, etc.).&lt;/p&gt;

&lt;p&gt;One of the arguments for expanding into backend/other technologies is that they reinforce and benefit your abilities as a developer. I think this is a good point, but it applies more broadly to any skill or hobby. A frontend engineer can broaden their experience through other means besides backend engineering, like activities, hobbies, or creative outlets. I'll highlight some of the ways my hobbies have made me a better engineer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing
&lt;/h2&gt;

&lt;p&gt;I'm working on my writing for a lot of reasons, but one reason is so I can better communicate with my teammates. As I've gotten further in my career, the marginal benefit of improving my individual coding ability has diminished, and my ability to communicate, negotiate, and prioritize within my team has become more important.&lt;/p&gt;

&lt;p&gt;Writing blog posts makes it easier to communicate my ideas and provides me with a resource to reference when people ask for my thoughts on something I'm familiar with. It has also driven several of my new projects as I have to brainstorm new article ideas each week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Photography
&lt;/h2&gt;

&lt;p&gt;I picked up photography to feel closer to my grandparents who were avid amateur photographers. While I'm not a great photographer, it helps develop my understanding of visual composition and color, making me better at building user interfaces. It also improves my attention to detail and ability to communicate with designers. Editing photos forces me to identify high value work and prioritize my best photos for processing in Lightroom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running
&lt;/h2&gt;

&lt;p&gt;Running and other forms of exercise keep me healthy and balance out sitting and standing at a desk all day. They also require growing past my limits through goal tracking and consistent habits. Running reduces my back pain and improves my mood, making me a more pleasant person to work with.&lt;/p&gt;

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

&lt;p&gt;Being a better engineer isn't only about learning the next design pattern, technique, or technology. For me, investing too much into that in the past has led to boredom and burnout, with no escape. Balancing my time with other hobbies helps me keep the creative spark going, as well as build my growth mindset. I'm not great at any of these hobbies compared to my coding ability, but building these skills has been an exercise in humility and recognition of my ability to grow.&lt;/p&gt;

&lt;p&gt;What hobbies have made you a better engineer? The connection doesn't have to be direct:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Has it improved your health?&lt;/li&gt;
&lt;li&gt;Made you a more helpful teammate?&lt;/li&gt;
&lt;li&gt;Allowed you to focus more deeply?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those all make you a better engineer! Hopefully if you're lucky enough to have a holiday break right now, you're taking time to recharge through other hobbies or interests. Happy Holidays!&lt;/p&gt;

</description>
      <category>balance</category>
      <category>health</category>
      <category>hobbies</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fighting Misery</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 20 Dec 2020 13:32:58 +0000</pubDate>
      <link>https://dev.to/dcwither/fighting-misery-3faf</link>
      <guid>https://dev.to/dcwither/fighting-misery-3faf</guid>
      <description>&lt;p&gt;As I write this I'm taking good care of myself, working towards contentment. This article serves to remind to my future self what tools I have at my disposal to help escape misery. I'm using misery as a stand-in for depression/anxiety/stress/low motivation because it's often hard for me to recognize which combination of these I'm experiencing and the end result is that I'm unable to experience contentment. Similarly, I'm using contentment as a stand-in for happiness/calm/high motivation.&lt;/p&gt;

&lt;p&gt;Before I start, I want to acknowledge that not everyone has all these tools at their disposal, and they certainly won't help everyone. This is also not a replacement for medication or help from a medical professional - in fact I was able to find most of these through therapy and coaching.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identifying and Maintaining Feelings
&lt;/h2&gt;

&lt;p&gt;One of the biggest challenges as I drift into misery is first identifying how I'm feeling. Knowing that I'm feeling unmotivated, upset, or disappointed in myself helps me build a plan to get to a better place. I use the following habits to maintain and monitor my mental health.&lt;/p&gt;

&lt;h3&gt;
  
  
  Achievement and Gratitude
&lt;/h3&gt;

&lt;p&gt;At the end of each day, my wife and I share at least one achievement and one gratitude. They can be anything, even everyday activities (e.g. one day my achievement was I went for a walk, and my gratitude was I called my family). This is a moment for me to pause and think about the good things in my life - sometimes I struggle to come up with even one. If this happens a few days in a row, it's a flag that I need to invest more time in my mental health.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cognitive Behavioral Therapy (CBT) Journaling
&lt;/h3&gt;

&lt;p&gt;To track my mood, I record my feelings with CBT journaling apps. I've used two apps for this - &lt;a href="https://www.ustwo.com/work/moodnotes" rel="noopener noreferrer"&gt;Mood Notes&lt;/a&gt; and &lt;a href="https://www.stoicroutine.com/" rel="noopener noreferrer"&gt;Stoic&lt;/a&gt;. Both have been great, though I recently switched to Stoic because I like the aesthetic. They force me to think about how I'm feeling, which I would otherwise overlook. They ask me questions that test my cognitive biases and have me come up with other ways of thinking about whatever may be bothering me. At times when I feel like I'm always miserable, my previous entries remind me that these feelings aren't actually constant.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exercise
&lt;/h3&gt;

&lt;p&gt;Every day after I get out of bed, I hop on my stationary bike with a goal of biking for 20 minutes. This isn't intense exercise and if I'm not feeling up for it, I allow myself to stop after 5 minutes. This is my keystone habit and a commitment I've made to myself, so it's a red flag if I skip this routine two days in a row.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intervening
&lt;/h2&gt;

&lt;p&gt;Once I've identified that I'm experiencing some form of misery, it's time to focus on recovering. I don't always start this step immediately, as taking action requires some amount of energy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tell Someone I'm Struggling
&lt;/h3&gt;

&lt;p&gt;The first thing that moves me towards contentment is telling people, often my wife and therapist/coach, that I'm struggling. Being vulnerable is hard for me, but it helps me to not feel alone. It also gives others context on what I'm dealing with and sets expectations for what I'm currently capable of handling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Work Through Guilt
&lt;/h3&gt;

&lt;p&gt;A common feeling that comes with my misery is guilt - guilt over not keeping up with routines, playing video games too much, eating unhealthily, not exercising, letting laundry pile up, etc. With my therapist's help, I've realized this feeling of guilt isn't motivating me, rather it's driving me to hide from these problems even more.&lt;/p&gt;

&lt;p&gt;Listening to this guilt and forgiving myself for these "failures" helps me calm the inner monologue telling me what I should have done. Taking the time to recognize my partial successes also helps me escape the all-or-nothing mindset that contributes to my guilt.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I didn't exercise, but I &lt;strong&gt;did&lt;/strong&gt; go for a walk.&lt;/li&gt;
&lt;li&gt;I didn't bike for 20 minutes, but I managed to get on the bike.&lt;/li&gt;
&lt;li&gt;I've been miserable and aware of it for a few weeks, but I told someone today and I'm working on it.&lt;/li&gt;
&lt;li&gt;I sat on the couch all day, but I remembered to do my CBT journaling.&lt;/li&gt;
&lt;li&gt;My other habits are flying out the window, but I've written for 7 weeks in a row.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Find Something I &lt;em&gt;Can&lt;/em&gt; Do
&lt;/h3&gt;

&lt;p&gt;From here I pick something within my current ability and commit to doing it. This can be whatever requires the least amount of energy to help build more motivation. CBT refers to this as &lt;a href="https://en.wikipedia.org/wiki/Behavioral_activation" rel="noopener noreferrer"&gt;behavioral activation&lt;/a&gt;. For me these activities are biking, going for a walk, cooking an easy meal, and sharing achievement and gratitude. Habits that fit into my typical routine are easier (e.g. biking is a morning thing for me, so doing it in the afternoon requires a lot more motivation).&lt;/p&gt;

&lt;p&gt;My friend and former classmate Jamie Wong wrote an amazing article on this topic, so I'll refer you to &lt;a href="http://jamie-wong.com/post/the-pit/" rel="noopener noreferrer"&gt;The Pit, the Cabin, and the Dance Floor&lt;/a&gt; for a deeper dive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Accountability
&lt;/h3&gt;

&lt;p&gt;At this point I've already told someone I'm struggling, so now I have to tell that person what I've committed to doing. Being specific makes this more effective, e.g. "I'm going to get out of bed by 7AM and bike for at least five minutes."&lt;/p&gt;

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

&lt;p&gt;My misery doesn't just disappear after following these steps, but this has been how I've been working through it lately. I've gone through the loop of misery and contentment a few times this year, and I've found it's been shorter than in the past. As with academic and professional pursuits, I'm trying to take a growth mindset towards mental health.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuugflnfu3e8co9t6emdq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fuugflnfu3e8co9t6emdq.gif" alt="Jake from Adventure Time Saying "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similar to how the goal of meditation isn't to have a clear mind at all times, my goal isn't to be content all the time. The goal is to shorten the times when I'm miserable and extend the times when I'm content.&lt;/p&gt;

</description>
      <category>mentalhealth</category>
      <category>productivity</category>
      <category>gratitude</category>
      <category>cbt</category>
    </item>
    <item>
      <title>Typography: Separating Style from Semantics</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 13 Dec 2020 12:26:20 +0000</pubDate>
      <link>https://dev.to/dcwither/typography-separating-style-from-semantics-n3f</link>
      <guid>https://dev.to/dcwither/typography-separating-style-from-semantics-n3f</guid>
      <description>&lt;p&gt;Getting started with accessibility can be intimidating, especially when your project has complicated user flows and interactions. A great entry point into accessibility is fixing up your heading hierarchy. Fixing this simple issue can make navigating the site much easier for your users.&lt;/p&gt;

&lt;p&gt;This article focuses on improving webpage accessibility in a small but meaningful way. For more information on headings and accessibility, consult the &lt;a href="https://www.w3.org/WAI/tutorials/page-structure/headings/"&gt;W3 Headings Accessibility Tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The intent of this Success Criterion is to ensure that information and relationships that are implied by visual or auditory formatting are preserved when the presentation format changes.&lt;/p&gt;

&lt;p&gt;- &lt;a href="https://www.w3.org/WAI/WCAG21/Understanding/info-and-relationships.html"&gt;WCAG Success Criterion 1.3.1: Info and Relationships&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Heading Hierarchy
&lt;/h2&gt;

&lt;p&gt;Correct heading hierarchy helps assistive technology like screen readers interpret your pages. Consistently applying styles with a 1:1 mapping between heading elements and size is the ideal scenario. On complex websites, though, design constraints often prevent us from consistently styling headings, so we're left with three options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styles &amp;gt; Hierarchy
&lt;/h3&gt;

&lt;p&gt;If we prioritize styles, we conform to design constraints but lose the heading hierarchy. Even worse, this results in misleading headings that explicitly miscommunicate the structure to screen readers and other assistive technology.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/typography-broken-headings-dj42o?module=index.html&amp;amp;view=split"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This use of headings produces a document structure that implies the first &lt;code&gt;h3&lt;/code&gt; (Life) isn't a parent heading of the subsequent &lt;code&gt;h2&lt;/code&gt; despite Kingdom being a grouping within Life - this isn't the intended outcome.&lt;/p&gt;

&lt;p&gt;This might not be a big deal for seeing users, but we want everyone to be able to fully navigate and experience the products we build, so we should avoid this practice.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;del&gt;Fight&lt;/del&gt; Work with Design to Make Styles = Hierarchy
&lt;/h3&gt;

&lt;p&gt;This option isn't always available, but you should apply it wherever possible. While not universal, text size indicates a visual hierarchy within the document, and making an &lt;code&gt;h3&lt;/code&gt; larger than an &lt;code&gt;h2&lt;/code&gt; can be misleading to the user.&lt;/p&gt;

&lt;p&gt;If your designer isn't familiar with accessibility standards, work with them to better understand heading structure and WCAG guidelines. Engineers can't own accessibility alone, and neither can designers - it's a shared responsibility for everyone to invest time and effort into. If everyone does their part, the product will be better for &lt;strong&gt;all&lt;/strong&gt; users.&lt;/p&gt;

&lt;p&gt;If you reach full alignment between engineers and designers, you end up with a heading structure that matches both visually and semantically.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/typography-consistent-headings-r3crz?module=index.html&amp;amp;view=split"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Styling Elements Independent from Semantics
&lt;/h3&gt;

&lt;p&gt;For the times we can't reach 100% alignment between engineering and design, we should still work to reach a helpful information hierarchy in our HTML. By separating appearance from the element tag, we can achieve the desired style while maintaining the correct heading hierarchy.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/typography-independent-styles-s1fjs?module=index.html&amp;amp;view=split"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;So how do we separate appearance from the element tag? The easiest solution is to use global classes that match the intended element:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* classes match the intended element */&lt;/span&gt;
&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;0.5em&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Poppins"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.5rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h3&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.75rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h4&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h4&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;h5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.h5&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&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;em&gt;Note: the styles here aren't specific recommendations, rather an example of what &lt;strong&gt;can&lt;/strong&gt; be done.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Existing Implementations
&lt;/h2&gt;

&lt;p&gt;While I've walked through a manual implementation with raw HTML and CSS, you may want a more robust solution. You could build one from scratch without much difficulty, but there are also existing solutions in component libraries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://material-ui.com/components/typography/"&gt;Material UI - Typography&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://react.semantic-ui.com/elements/header/"&gt;Semantic UI - Header&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Hopefully this tutorial helped you learn a bit more about accessibility in web development and how to incorporate it into your regular development practices. If you've come up with your own solution for this topic, please share it below.&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Automated Heading Tags
&lt;/h2&gt;

&lt;p&gt;Still here? Okay, let's go a little further. What if I use a component in two separate parts of the app - one where the parent heading is an &lt;code&gt;h1&lt;/code&gt; and another where the parent heading is an &lt;code&gt;h2&lt;/code&gt; - what should be the child heading tag in this component?&lt;/p&gt;

&lt;p&gt;I might argue that in the first scenario it should be an &lt;code&gt;h2&lt;/code&gt; and in the second it should be an &lt;code&gt;h3&lt;/code&gt;. Should we do a custom heading prop? 🤢 Let's try solving it with React context instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Heading.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headingLevelContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&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;useHeadingLevelContext&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;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;headingLevelContext&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Heading&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&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;defaultComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`h&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;useHeadingLevelContext&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;HeadingComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;defaultComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HeadingComponent&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;HeadingComponent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HeadingComponent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;HeadingBlock&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;children&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;defaultLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useHeadingLevelContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;headingLevelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;defaultLevel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;headingLevelContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;HeadingBlock&lt;/code&gt; keeps track of the heading level by incrementing the heading level context when nested inside another &lt;code&gt;HeadingBlock&lt;/code&gt; and increasing the heading tag used by &lt;code&gt;Heading&lt;/code&gt;. Let's look at it in action:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/automatic-heading-level-olroi?module=src/App.js&amp;amp;view=split"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;By capturing the heading level in the context, we can provide the size for styling, while the context determines the heading element used.&lt;/p&gt;

&lt;p&gt;Abstracting heading semantics away from developers may also have downsides, and I haven't tested it in a production application or accessibility audit, but it's an interesting idea to explore further.&lt;/p&gt;

</description>
      <category>css</category>
      <category>a11y</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Dev.to Writing Process</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 06 Dec 2020 12:11:09 +0000</pubDate>
      <link>https://dev.to/dcwither/my-dev-to-writing-process-2dng</link>
      <guid>https://dev.to/dcwither/my-dev-to-writing-process-2dng</guid>
      <description>&lt;p&gt;Beginning to write Dev.to articles was intimidating for me, and a tutorial on writing articles would have helped reduce the barrier to entry. Now that I've written six articles, I've settled into a comfortable flow that I thought I'd share with anyone else interested in taking the plunge into writing their own technical articles.&lt;/p&gt;

&lt;p&gt;I've tuned this process for my needs as a writer, ensuring I can consistently write technical articles with a specific structure, but I think it works well for opinion pieces as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;I keep all my articles in a GitHub repository so that I can access them from multiple computers and track changes as I go. Like coding, sometimes I'm unsatisfied with my changes, and I want to compare them to a previous version. A simple file version manager would also suffice.&lt;/p&gt;

&lt;h3&gt;
  
  
  File Structure
&lt;/h3&gt;

&lt;p&gt;I keep my active articles in a &lt;code&gt;drafts&lt;/code&gt; folder. When I publish an article, I prepend it with the date and move it to the &lt;code&gt;published&lt;/code&gt; folder for ordering/archival purposes. This gives me a record of my articles and images in case I want to publish anywhere else or need to make a change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;├── README.md
├── drafts
│   ├── more_accessible
│   │   └── main.md
│   └── writing_process
│       └── main.md
├── package-lock.json
├── package.json
└── published
    ├── 2020_10_31_color_migration
    │   ├── hero.jpg
    │   └── main.md
    ├── 2020_11_07_testing_postcss
    │   ├── hero.jpg
    │   └── main.md
    ├── 2020_11_15_marginal_linting
    │   ├── hero.jpg
    │   ├── main.md
    │   └── reviewdog-annotation.png
    ├── 2020_11_16_use_datetime
    │   └── main.md
    ├── 2020_11_22_testing_time
    │   ├── hero.jpg
    │   └── main.md
    └── 2020_11_29_bad_variable_names
        ├── hero.png
        └── main.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Package.json
&lt;/h3&gt;

&lt;p&gt;I use Prettier with &lt;code&gt;lint-staged&lt;/code&gt; to automatically format my articles after every commit. This is particularly helpful for any code blocks in &lt;code&gt;.md&lt;/code&gt; files.&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;package.json&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;dependencies&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;scripts&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;necessary&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;commit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;hooks&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;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier --ignore-path=.gitignore **/*.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint:fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run lint -- --write"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"husky"&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;"hooks"&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;"pre-commit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"lint-staged"&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;"lint-staged"&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;"*.{js,jsx,css,scss,md}"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prettier --write"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"devDependencies"&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;"husky"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^4.3.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"lint-staged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^10.5.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"prettier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^2.1.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;h3&gt;
  
  
  VSCode
&lt;/h3&gt;

&lt;p&gt;I use &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; to edit and preview markdown. I've tried &lt;a href="https://marktext.app/" rel="noopener noreferrer"&gt;Mark Text&lt;/a&gt;, and &lt;a href="https://typora.io/" rel="noopener noreferrer"&gt;Typora&lt;/a&gt;, neither of which felt right for me - their rich text editing for markdown seems to struggle with undo/redo.&lt;/p&gt;

&lt;p&gt;I use the following VSCode Plugins to help with my writing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=ban.spellright" rel="noopener noreferrer"&gt;Spell Right&lt;/a&gt; for basic spell checking.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;VSCode Prettier&lt;/a&gt; auto-formats my code on every save.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://marketplace.visualstudio.com/items?itemName=travisthetechie.write-good-linter" rel="noopener noreferrer"&gt;Write Good&lt;/a&gt; encourages me to use active voice and cuts unnecessary words.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's my project level &lt;code&gt;.vscode/settings.json&lt;/code&gt; for configuring the plugins:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.rulers"&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="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;"[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;"editor.wordWrap"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bounded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"editor.wordWrapColumn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"editor.formatOnSave"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"write-good.languages"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"markdown"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"plaintext"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"languageToolLinter.serviceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"public"&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;h2&gt;
  
  
  The Process
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Ideas 💡
&lt;/h3&gt;

&lt;p&gt;I initially struggled each week to come up with new article ideas, so I started tracking my thoughts as they popped into my head throughout the day. As I come up with ideas I add them as sections to &lt;code&gt;README.md&lt;/code&gt;. To develop the idea, I add bullet points that I'd like to cover. Often these bullet points become the headings for my articles. If my computer isn't near, I add a short note on my phone to move to my &lt;code&gt;README.md&lt;/code&gt; later.&lt;/p&gt;
&lt;h3&gt;
  
  
  Structure 🏗
&lt;/h3&gt;

&lt;p&gt;When I get started on an article, I try to get the heading structure onto the page first. This helps me identify what I want to say and make sure I'm making an honest effort at having a flow to my article. At this point I run the structure by my editor (in my case, my wife, but whomever you have to get a second opinion can help).&lt;/p&gt;
&lt;h3&gt;
  
  
  First Draft 📝
&lt;/h3&gt;

&lt;p&gt;Now that I have the idea and structure, it's time to write the article. In addition to the article itself, I build demos or test applications to verify technical accuracy. For instance, I made &lt;a href="https://www.npmjs.com/package/jest-postcss" rel="noopener noreferrer"&gt;&lt;code&gt;jest-postcss&lt;/code&gt;&lt;/a&gt; while writing &lt;a href="https://dev.to/dcwither/writing-cleaner-tests-with-jest-extensions-5fmb"&gt;Writing Cleaner Tests with Jest Extensions&lt;/a&gt; and an example repo for &lt;a href="https://dev.to/dcwither/adding-new-lint-rules-without-the-fuss-34a2"&gt;Adding New Lint Rules Without the Fuss&lt;/a&gt;. I embed interactive tools (repl.it, Code Sandbox) whenever possible through Dev.to's &lt;a href="https://docs.dev.to/frontend/liquid-tags/"&gt;liquid tags&lt;/a&gt; to make the article more engaging.&lt;/p&gt;
&lt;h3&gt;
  
  
  Read Through 👀
&lt;/h3&gt;

&lt;p&gt;I do a first pass to make sure everything makes sense to me. A lot of big edits and paragraph rearranging happen in this step. I try to leave the first draft sitting for a few hours or a day so I can come back to it with a fresh mind.&lt;/p&gt;
&lt;h3&gt;
  
  
  Listen to it 🎧
&lt;/h3&gt;

&lt;p&gt;Because I struggle to see what is wrong with my writing just by reading it, I use &lt;a href="https://support.apple.com/guide/mac-help/have-your-mac-speak-text-thats-on-the-screen-mh27448/mac" rel="noopener noreferrer"&gt;Mac's text to speech&lt;/a&gt; to listen to my article. This helps catch phrasing that is hard to parse or sounds awkward when read aloud. This and the read through help limit the time my editor puts into the writing - she's doing it for free after all.&lt;/p&gt;
&lt;h3&gt;
  
  
  High-Level Feedback 🚥
&lt;/h3&gt;

&lt;p&gt;At this point I ask my editor to look through the article for any big changes or gaps in the article. I try to not be too attached to anything I've written so far because anything is fair game for revisions.&lt;/p&gt;
&lt;h3&gt;
  
  
  Polish ✨
&lt;/h3&gt;

&lt;p&gt;After addressing high-level comments, I ask my editor to walk through the entire article with me piece-by-piece to clean up wording. This is where we make sure the ideas are easy to understand for the reader. We no longer spend the whole time fixing passive voice now that I use the Write Good plugin.&lt;/p&gt;
&lt;h3&gt;
  
  
  Ship It! 🚢
&lt;/h3&gt;

&lt;p&gt;For now I publish directly to Dev.to by copying my markdown files over, making sure to preview them for any issues. It's important to make sure liquid tags work before publishing since they don't render in VSCode's markdown preview.&lt;/p&gt;
&lt;h2&gt;
  
  
  Hero Images
&lt;/h2&gt;

&lt;p&gt;Hero images help catch the reader's eye. They're an opportunity to show readers something about your article or yourself. I normally use a photo I've taken myself, but for articles with code I started using &lt;a href="https://carbon.now.sh/" rel="noopener noreferrer"&gt;Carbon.now.sh&lt;/a&gt; to help give readers a clearer idea of the article's contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://carbon.now.sh/LFkH9XtlBux9mHjyx69A" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fegoeys5t6p8j2nbdt4jq.png" alt="example carbon screenshot"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;This was the hero image from my article, &lt;a href="https://dev.to/dcwither/stop-using-data-as-a-variable-name-3954"&gt;Stop Using "data" as a Variable Name&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's my whole process for writing technical articles. I hope it's helpful to anyone who wants to start writing and makes the process less intimidating. I'd love to hear what other tools, processes, or services people use for their writing.&lt;/p&gt;

&lt;p&gt;You can also check out my template repo, which I'll try to keep up-to-date with any new practices I adopt.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dcwither" rel="noopener noreferrer"&gt;
        dcwither
      &lt;/a&gt; / &lt;a href="https://github.com/dcwither/articles-template" rel="noopener noreferrer"&gt;
        articles-template
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      template folder
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Ideas&lt;/h1&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Easy&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Previous Projects&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Future Projects&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Opinion&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Big Ideas&lt;/h2&gt;

&lt;/div&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dcwither/articles-template" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>writing</category>
      <category>tutorial</category>
      <category>markdown</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Stop Using "data" as a Variable Name</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 29 Nov 2020 12:41:02 +0000</pubDate>
      <link>https://dev.to/dcwither/stop-using-data-as-a-variable-name-3954</link>
      <guid>https://dev.to/dcwither/stop-using-data-as-a-variable-name-3954</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;"There are only two hard things in Computer Science: cache invalidation and naming things."&lt;/p&gt;

&lt;p&gt;- Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Setting aside cache invalidation, which is indeed difficult, this infamous quote is something that rings in my head whenever I'm having trouble finding the right name for something. Clear naming provides important context whenever someone needs to quickly understand code, whether they're firefighting, debugging, interviewing, or assisting a teammate - I don't have to ask someone what &lt;code&gt;users&lt;/code&gt; means, but I do have to ask what &lt;code&gt;data&lt;/code&gt; means. While I don't often find the best names, I try to optimize my code for the reader by following some basic rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Rules:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use Meaningful Prefixes
&lt;/h3&gt;

&lt;p&gt;While these prefixes aren't universal, they are great to establish a shared language within your team. Using them consistently throughout your codebase can make reading comprehension easier.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;get&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;fetch&lt;/code&gt; for functions that return a value or a &lt;code&gt;Promise&lt;/code&gt; that resolves to a value without mutating arguments or itself.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;set&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt; for functions that mutate arguments or the callee for member functions. These functions may also return the updated value or a &lt;code&gt;Promise&lt;/code&gt; that resolves to the updated value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;on&lt;/code&gt;, &lt;code&gt;handle&lt;/code&gt; for event handler functions. My team's convention is that &lt;code&gt;onEvent&lt;/code&gt; is passed through props into the component and &lt;code&gt;handleEvent&lt;/code&gt; is declared inside the component.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;is&lt;/code&gt;, &lt;code&gt;should&lt;/code&gt;, &lt;code&gt;can&lt;/code&gt; for boolean variables and functions with boolean return values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Any convention that becomes a standard in your team can help with readability. Make sure to document these in the project &lt;code&gt;README&lt;/code&gt; or wiki. Creating custom linters to enforce these would be even more effective.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Words that Add Meaning
&lt;/h3&gt;

&lt;p&gt;As an example, developers often name variables &lt;code&gt;data&lt;/code&gt; by default, but let's examine a couple of its &lt;a href="https://www.merriam-webster.com/dictionary/data"&gt;definitions&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;"factual information (such as measurements or statistics) used as a basis for reasoning, discussion, or calculation"&lt;/li&gt;
&lt;li&gt;"information in digital form that can be transmitted or processed"&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;These definitions could refer to any variable we process, so they give the reader &lt;strong&gt;no information&lt;/strong&gt;. Let's look at an example that doesn't follow this rule:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;total&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="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;total&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;total&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;value&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;total&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;We know this function calculates a total of something, but we're not sure what.&lt;/p&gt;

&lt;h4&gt;
  
  
  Exceptions
&lt;/h4&gt;

&lt;p&gt;Sometimes your variable could actually contain anything, like a network request response body. Libraries like &lt;a href="https://github.com/axios/axios"&gt;axios&lt;/a&gt; use &lt;code&gt;data&lt;/code&gt;, which is a reasonable name in this context. Even in this scenario, the alternative &lt;code&gt;body&lt;/code&gt; conveys more meaning and is what the native web API &lt;code&gt;fetch&lt;/code&gt; uses in its &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Response"&gt;Response&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Full Words
&lt;/h3&gt;

&lt;p&gt;Like everyone else's, the &lt;a href="https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow#Two_systems"&gt;System 1&lt;/a&gt; part of my brain always tells me to take shortcuts to finish my task sooner. When it comes to variable naming, shortcuts often mean abbreviations or single character variable names.&lt;/p&gt;

&lt;p&gt;Like before, let's look at a function that doesn't follow the rule:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;totBal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accts&lt;/span&gt;&lt;span class="p"&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;tot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&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;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;accts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;tot&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;accts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;bal&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;tot&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;We can do some mental gymnastics to guess that &lt;code&gt;accts&lt;/code&gt; means &lt;code&gt;accounts&lt;/code&gt; and &lt;code&gt;tot&lt;/code&gt; is &lt;code&gt;total&lt;/code&gt;, but we can't process the code at a glance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Exceptions
&lt;/h4&gt;

&lt;p&gt;Common industry abbreviations are preferred over their long form (e.g. URL, API, CSS).&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't Use "Fluff" Words
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Container&lt;/code&gt; and &lt;code&gt;Wrapper&lt;/code&gt; only have meaning in relation to the thing they're containing. The problem is that any component that isn't a base element contains another component. You also end up in the awkward position of naming components &lt;code&gt;MyComponentContainerContainer&lt;/code&gt;. The same applies to &lt;code&gt;Wrapper&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Exceptions
&lt;/h4&gt;

&lt;p&gt;In some contexts, these "fluff" words can have significant meaning. A common pattern in React class components is the &lt;a href="https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0"&gt;presentation/container component pattern&lt;/a&gt;. &lt;code&gt;Container&lt;/code&gt; in this case may indicate a component that manages state on behalf of a presentation component - just make sure you consistently use it for this purpose, or it loses meaning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spelling Matters
&lt;/h3&gt;

&lt;p&gt;Misspelling words creates bugs and makes searching your code harder. Typos are easy to ignore, but having the right spelling for everything in your codebase makes a world of difference, especially when attempting global find/replace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it Together
&lt;/h2&gt;

&lt;p&gt;Applying all the rules at once, we get the following function:&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getAccountsTotalBalance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&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;totalBalance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;for&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;accountIndex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;accountIndex&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;accountIndex&lt;/span&gt;&lt;span class="o"&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;totalBalance&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;accounts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;accountIndex&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;balance&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;totalBalance&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;While &lt;code&gt;accountIndex&lt;/code&gt; vs. &lt;code&gt;i&lt;/code&gt; might be contentious, the rest of the function should be much clearer. &lt;code&gt;getAccountsTotalBalance&lt;/code&gt; fully communicates the intent of the function and the prefix &lt;code&gt;get&lt;/code&gt; indicates that it will not result in any mutations. It's worth the code author investing increased effort in exchange for the benefit of the reader. Your future self will appreciate the extra work when they're maintaining the code six months later.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you're worried about line length, consider using a tool like Prettier to automatically format the code.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;The goal of these rules is to bring as much meaning as possible to the code we write for future readers. Find the ones that work for your context, and if a rule is doing more harm than good, change or abandon it. Codifying your team's rules will help communicate your thoughts on the subject and is not meant to bring a hammer down on your teammates.&lt;/p&gt;

&lt;p&gt;Please share any other rules you follow when naming variables, functions, classes, etc. or let me know if you disagree with any of the rules here and how you'd change them.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>javascript</category>
      <category>react</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Defusing Time Bomb Tests using Randomization</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 22 Nov 2020 22:45:00 +0000</pubDate>
      <link>https://dev.to/dcwither/defusing-time-bomb-tests-using-randomization-561l</link>
      <guid>https://dev.to/dcwither/defusing-time-bomb-tests-using-randomization-561l</guid>
      <description>&lt;p&gt;Flaky tests are generally something to avoid, but even worse are tests that start failing a year after they merge. These tests are time bombs waiting to go off when we're least prepared (e.g. in the new year). What if adding chaos into our test environments actually helped surface these issues earlier in the development cycle?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: All examples are written for Jest tests, but the concepts can be adapted for other testing frameworks/languages.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Time in Tests
&lt;/h2&gt;

&lt;p&gt;You may have already written a test that worked when you wrote it and even worked in CI, but it stopped working at a later date - especially around New Year's when offices are closed.&lt;/p&gt;

&lt;p&gt;Here's an example:&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;// relies-on-time.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isInPast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&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="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// relies-on-time.test.js&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relies-on-time#isInPast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return false for dates in the future&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isInPast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2020-12-31T00:00:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At time of writing, this test passes, but on Jan 1, 2021, the test will begin failing. This isn't ideal because this code could have merged long ago. By the time it begins failing, it may block application deployments and we've lost some context on the intended behavior.&lt;/p&gt;
&lt;h2&gt;
  
  
  Using Fake Timers
&lt;/h2&gt;

&lt;p&gt;One solution is to use fake timers to mock the date. If you've spent a lot of time writing Jest tests, you've even likely mocked &lt;code&gt;Date.now&lt;/code&gt; to set the current date.&lt;/p&gt;

&lt;p&gt;As of &lt;a href="https://github.com/facebook/jest/blob/master/CHANGELOG.md#2610"&gt;Jest 26.1.0&lt;/a&gt; it includes the &lt;code&gt;jest.setSystemTime(now?: number | Date)&lt;/code&gt; function to mock the system time as long as you're using the &lt;code&gt;"modern"&lt;/code&gt; setting.&lt;/p&gt;

&lt;p&gt;Using fake timers and &lt;code&gt;jest.setSystemTime&lt;/code&gt;, we have a test that will now pass regardless of the current date:&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;// relies-on-time.test.js&lt;/span&gt;
&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;relies-on-time#isInPast&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should return false for dates in the future&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useFakeTimers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSystemTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2020-11-20T00:00:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isInPast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2020-12-31T00:00:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))).&lt;/span&gt;&lt;span class="nx"&gt;toBe&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We've caught this test and fixed it, but there are still other tests with the same issue waiting to blow up. People are also forgetful - we'll write more tests that pass now but won't pass the next day/week/month/year, so what do we do?&lt;/p&gt;
&lt;h2&gt;
  
  
  Random Time as a Default
&lt;/h2&gt;

&lt;p&gt;We can make use of randomization to get our tests to fail &lt;strong&gt;now&lt;/strong&gt; instead of later. If we default time as a random time that changes per run, we can take advantage of this randomization to detect tests that rely on a certain real-world time. This also helps the team build good habits of addressing time in tests.&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;// jest.setup.js&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;randomDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Source: https://stackoverflow.com/questions/9035627/elegant-method-to-generate-array-of-random-dates-within-two-dates&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&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="nx"&gt;random&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="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&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;beforeEach&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="c1"&gt;// setup our fake timers&lt;/span&gt;
  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useFakeTimers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;modern&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setSystemTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;randomDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2000-01-01T00:00:00.000Z&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2040-01-01T00:00:00.000Z&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="nx"&gt;afterEach&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="c1"&gt;// cleanup fake timers&lt;/span&gt;
  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runOnlyPendingTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;useRealTimers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Note: I chose Jan 1, 2000, to Jan 1, 2040, as an appropriate range for my application. You should pick the range that best fits your needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can add this snippet to your &lt;a href="https://jestjs.io/docs/en/configuration#setupfilesafterenv-array"&gt;global jest setup&lt;/a&gt;. After several runs, it will identify any tests that implicitly rely on time.&lt;/p&gt;
&lt;h3&gt;
  
  
  Catching Issues Sooner
&lt;/h3&gt;

&lt;p&gt;You should make sure that this check can flag issues before they merge. For example, you could add redundancy to test runs before PR merge by running them multiple times in CI.&lt;/p&gt;
&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Fake timers are great, but enforcing their use can be tricky. Time can be deeply embedded in our applications in ways we don't immediately see. Take advantage of randomization to root out implicit assumptions, and make them explicit so your tests can withstand the test of time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Appendix: Full Setup
&lt;/h2&gt;

&lt;p&gt;You can take a look at the whole setup with some more tests in this &lt;a href="https://github.com/dcwither/testing-time-randomization-example"&gt;example GitHub repo&lt;/a&gt;, or browse this repl.it:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sorry, repl.it embeds don't let me choose the default file. Check out &lt;code&gt;jest.setup.js&lt;/code&gt; and &lt;code&gt;relies-on-time.test.js&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag__replit"&gt;
  &lt;iframe height="550px" src="https://repl.it/@dcwither/testing-time-randomization?lite=true"&gt;&lt;/iframe&gt;
&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>testing</category>
      <category>time</category>
      <category>replit</category>
    </item>
    <item>
      <title>Tracking Time with React Hooks</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Tue, 17 Nov 2020 00:18:37 +0000</pubDate>
      <link>https://dev.to/dcwither/tracking-time-with-react-hooks-4b8b</link>
      <guid>https://dev.to/dcwither/tracking-time-with-react-hooks-4b8b</guid>
      <description>&lt;h2&gt;
  
  
  Let's Talk About Time
&lt;/h2&gt;

&lt;p&gt;Time is super tricky to account for in software, and one of the most common issues in frontend applications is developers forget that time keeps passing when the page is open.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Take a look at &lt;a href="https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time"&gt;Falsehoods programmers believe about time&lt;/a&gt; for a starting point on the assumptions developers make.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's really common to write a component that looks like this:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formatter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-us&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;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hour&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;minute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;second&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;numeric&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyDateComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&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;Date&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;formatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&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;See &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat"&gt;MDN Intl.DateTimeFormat&lt;/a&gt; for more on the date formatter used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The issue with this component is that it doesn't update when the seconds change. This isn't as much an issue if we're not displaying seconds, but even hours and days can pass while browser tabs are open.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;useDateTime&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;To solve this problem, I wrote &lt;code&gt;useDateTime&lt;/code&gt;, a React hook that tracks the time to a specified precision (second/minute/hour/day), triggering a state change on each &lt;code&gt;tick&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;useDateTime&lt;/code&gt; to fix &lt;code&gt;MyDateComponent&lt;/code&gt;, we get the following:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyDateComponent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useDateTime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;second&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// second | minute | hour | day&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;formatter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&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;This component now updates every second, keeping it accurate. We probably only want updates for every second in a clock component, and should avoid this frequency of updates for expensive renders. Outside of clocks, updating by the hour/day is much more common and something we should plan for as frontend engineers.&lt;/p&gt;

&lt;p&gt;You can take a look at the implementation of &lt;code&gt;useDateTime&lt;/code&gt; in this codesandbox:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/usedatetime-example-icfmq?module=/src/use-datetime.js&amp;amp;view=editor"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The implementation uses &lt;a href="https://github.com/date-fns/date-fns"&gt;&lt;code&gt;date-fns&lt;/code&gt;&lt;/a&gt; but could be rewritten with any date library (e.g. Moment/Luxon/Day.js)&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A similar hook could be used to track relative times by modifying &lt;code&gt;msUntilNext&lt;/code&gt; to determine the next &lt;code&gt;tick&lt;/code&gt; in increasing intervals.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Disclaimer
&lt;/h3&gt;

&lt;p&gt;This component &lt;em&gt;attempts&lt;/em&gt; to update immediately after the next &lt;code&gt;tick&lt;/code&gt; at the specified precision. Javascript's &lt;code&gt;setTimeout&lt;/code&gt; API does not guarantee that the timeout will trigger precisely on the target time, so the precision of this hook is also limited. Here's a good &lt;a href="https://stackoverflow.com/questions/29971898/how-to-create-an-accurate-timer-in-javascript"&gt;Stack Overflow Q&amp;amp;A&lt;/a&gt; summarizing this problem and a workaround.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>time</category>
      <category>datefns</category>
    </item>
    <item>
      <title>Adding New Lint Rules Without the Fuss</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Sun, 15 Nov 2020 15:57:38 +0000</pubDate>
      <link>https://dev.to/dcwither/adding-new-lint-rules-without-the-fuss-34a2</link>
      <guid>https://dev.to/dcwither/adding-new-lint-rules-without-the-fuss-34a2</guid>
      <description>&lt;p&gt;Linters are great for maintaining code quality and encoding team conventions, but how do you add new rules that your codebase currently violates? If there's a handful of violations or the violations can be autofixed, then it may be easy to fix them before adding the rule, but what if there are hundreds of them?&lt;/p&gt;

&lt;h2&gt;
  
  
  Case Study
&lt;/h2&gt;

&lt;p&gt;Suppose we've already set up CI for linting and want to add the ESLint rule &lt;a href="https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/extensions.md" rel="noopener noreferrer"&gt;&lt;code&gt;import/extensions&lt;/code&gt;&lt;/a&gt; to ensure that every import has a file extension. Let's walk through some options at our disposal and consider the pros and cons of each option.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 1: Fix Every Violation
&lt;/h2&gt;

&lt;p&gt;The first option available is to fix every violation that comes up from the new lint rule.&lt;/p&gt;

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

&lt;p&gt;First, add the new rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.eslintrc.json b/.eslintrc.json
&lt;/span&gt;   ...
   "rules": {
&lt;span class="gi"&gt;+    "import/extensions": ["error", "always"]
&lt;/span&gt;   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's now lint errors and we can't merge to our main branch without failing CI, so we fix every error before merging. While time consuming, this process is straightforward. You go through each lint violation in the codebase and fix it - in this case, by adding a file extension to every import that is missing one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;p&gt;The codebase is 100% adhering to the new rule! There are no lint violations, and everyone in the future will follow this rule in their changes or face the wrath of a failing build. This strategy is awesome when there's time and motivation to get it done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;p&gt;When there are hundreds of warnings that can't be autofixed, this strategy will postpone or prevent you from getting value out of new rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 2: Make the New Rule a Warning
&lt;/h2&gt;

&lt;p&gt;What about adding the new rule as a warning instead of an error?&lt;/p&gt;

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

&lt;p&gt;First, add our new rule:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.eslintrc.json b/.eslintrc.json
&lt;/span&gt;   ...
   "rules": {
&lt;span class="gi"&gt;+    "import/extensions": ["warn", "always"]
&lt;/span&gt;   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;and we're done!&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;p&gt;Setup was super easy - there's now a new lint rule that developers will see in their editors if they use an ESLint plugin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;p&gt;There's nothing &lt;em&gt;really&lt;/em&gt; enforcing the new rule. It's just a warning, and there may be hundreds of other warnings in the codebase. Warnings will pile up without developers noticing them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mitigations
&lt;/h4&gt;

&lt;p&gt;ESLint has a CLI option &lt;a href="https://eslint.org/docs/user-guide/command-line-interface#options" rel="noopener noreferrer"&gt;&lt;code&gt;--max-warnings&lt;/code&gt;&lt;/a&gt; which enforces a max number of warnings. Unfortunately, as you fix any existing warnings you have to keep this up to date, otherwise each fix gives slack for future warnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 3: Suppress the ESLint Errors
&lt;/h2&gt;

&lt;p&gt;We could suppress the existing violations to enforce the new rule going forward while avoiding the immediate cost of fixing existing issues.&lt;/p&gt;

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

&lt;p&gt;We'll add the new rule, and then add &lt;code&gt;eslint-disable-next-line&lt;/code&gt; for each lint violation.&lt;/p&gt;

&lt;p&gt;First, add the lint changes to &lt;code&gt;.eslintrc.json&lt;/code&gt;, same as option 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.eslintrc.json b/.eslintrc.json
&lt;/span&gt;   ...
   "rules": {
&lt;span class="gi"&gt;+    "import/extensions": ["error", "always"]
&lt;/span&gt;   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run &lt;a href="https://www.npmjs.com/package/suppress-eslint-errors" rel="noopener noreferrer"&gt;&lt;code&gt;suppress-eslint-errors&lt;/code&gt;&lt;/a&gt;. The &lt;code&gt;suppress-eslint-errors&lt;/code&gt; package notes that you may have to manually fix some of the suppressions it adds. If your setup doesn't involve ESLint, you'll need to find an alternative to &lt;code&gt;suppress-eslint-errors&lt;/code&gt;, or may have to do this part manually.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx suppress-eslint-errors src/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.&lt;span class="o"&gt;{&lt;/span&gt;ts,tsx&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"TODO: add extension"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/src/App.test.tsx b/src/App.test.tsx
&lt;/span&gt; import { render, screen } from '@testing-library/react
&lt;span class="gi"&gt;+// TODO: add extension
+// eslint-disable-next-line import/extensions
&lt;/span&gt; import App from './App';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;See &lt;a href="https://github.com/dcwither/marginal-linting-example/commit/f6de3211294f9751feb1a7ecc6df5c241830d38d" rel="noopener noreferrer"&gt;this example of a suppress violations diff&lt;/a&gt; for the changes needed to add this to a small project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;p&gt;Suppressing existing failures keeps our lint warnings clean and allows us to enforce future changes don't violate the new rule. You can go back and systematically fix suppressed violations in smaller chunks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;p&gt;The lines suppressing warnings reduce the signal-to-noise ratio of the code. It can also make it seem ok to add &lt;code&gt;eslint-disable&lt;/code&gt; whenever a developer writes code that violates lint rules, reducing the effectiveness of linting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Option 4: Only Lint New Changes with New Rules
&lt;/h2&gt;

&lt;p&gt;With a little extra work and a slightly more complicated setup, we can achieve linting that will ignore existing violations, while keeping us accountable in new changes. I like to call this &lt;strong&gt;marginal linting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Using a tool like &lt;a href="https://github.com/reviewdog/reviewdog" rel="noopener noreferrer"&gt;reviewdog&lt;/a&gt; (or &lt;a href="https://github.com/prontolabs/pronto" rel="noopener noreferrer"&gt;pronto&lt;/a&gt; if you like ruby), we can set up GitHub checks to annotate our PRs with any lint violations.&lt;/p&gt;

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

&lt;p&gt;We'll have two separate ESLint configs now. The "main" ESLint config (&lt;code&gt;.eslintrc.json&lt;/code&gt;) adds the new rule. This is our default config which we run in editors as well as in reviewdog.&lt;/p&gt;

&lt;p&gt;First, add the lint changes to &lt;code&gt;.eslintrc.json&lt;/code&gt;, same as option 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gh"&gt;diff --git a/.eslintrc.json b/.eslintrc.json
&lt;/span&gt;   ...
   "rules": {
&lt;span class="gi"&gt;+    "import/extensions": ["error", "always"]
&lt;/span&gt;   }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our second ESLint config will intentionally disable the newly added rule in CI. Target it in the lint workflow with &lt;code&gt;eslint --config=.eslint-ci.json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;.eslintrc-ci.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;"extends"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".eslintrc.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;"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;"import/extensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"off"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add a new GitHub workflow using the &lt;a href="https://github.com/reviewdog/action-eslint" rel="noopener noreferrer"&gt;reviewdog eslint action&lt;/a&gt; to execute our new rules on pull requests.&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/reviewdog.yml&lt;/span&gt;
&lt;span class="c1"&gt;# Modified from reviewdog action eslint README&lt;/span&gt;
&lt;span class="c1"&gt;# https://github.com/reviewdog/action-eslint#githubworkflowsreviewdogyml&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;reviewdog&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;eslint&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;runner / eslint&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@v1&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 Typescript Changes 👕&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;reviewdog/action-eslint@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;reporter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github-pr-check&lt;/span&gt;
          &lt;span class="na"&gt;eslint_flags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--config=.eslintrc.json&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;src/**/*.{ts,tsx}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;See &lt;a href="https://github.com/dcwither/marginal-linting-example/commit/29310a40438f387f042e7db85d5b4124b7183a0a" rel="noopener noreferrer"&gt;this example reviewdog setup diff&lt;/a&gt; for the changes needed to add this to a small project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  The Result
&lt;/h3&gt;

&lt;p&gt;We now receive warnings in our pull requests whenever our changes violate any lint rules, including our existing ones.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fphrsb924pyfu44xd784y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fphrsb924pyfu44xd784y.png" alt="ESLint error annotation appearing in GitHub pull request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Pros
&lt;/h3&gt;

&lt;p&gt;Making &lt;code&gt;.eslintrc.json&lt;/code&gt; the more restrictive configuration ensures that any new integrations will follow it by default. Any use of &lt;code&gt;.eslintrc-ci.json&lt;/code&gt; can be explicitly specified such as in CI.&lt;/p&gt;

&lt;p&gt;This setup has the added benefit of including code review integration, which can be beneficial regardless of new lint rules. It also means that any new rules require a two line change: one for the lint rule in &lt;code&gt;.eslintrc.json&lt;/code&gt; and another to disable it in &lt;code&gt;.eslintrc-ci.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cons
&lt;/h3&gt;

&lt;p&gt;The setup for this option is more involved, and adds a new technology to the CI stack. The build time for this task in a new &lt;code&gt;create-react-app&lt;/code&gt; was 3 minutes, and could increase depending on the project size.&lt;/p&gt;

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

&lt;p&gt;While it's nice to have a 100% compliant codebase, it might not be possible to fix every violation immediately. Minimizing the effort of adding new lint rules helps ensure your team can adopt and enforce best practices moving forward.&lt;/p&gt;

&lt;p&gt;Running a script to disable lint errors for new rules can quickly fix the issue but remains the same effort for every future lint rule. Adopting two lint configs, while requiring a slightly more complex initial setup, provides the same benefit and allows you to add new rules easily. Integrating it with reviewdog or pronto makes encouraging the new practices even easier in code review.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>github</category>
      <category>codequality</category>
      <category>linting</category>
    </item>
    <item>
      <title>Writing Cleaner Tests with Jest Extensions</title>
      <dc:creator>Devin Witherspoon</dc:creator>
      <pubDate>Mon, 09 Nov 2020 15:21:41 +0000</pubDate>
      <link>https://dev.to/dcwither/writing-cleaner-tests-with-jest-extensions-5fmb</link>
      <guid>https://dev.to/dcwither/writing-cleaner-tests-with-jest-extensions-5fmb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;While developing &lt;a href="https://www.npmjs.com/package/scss-codemods"&gt;scss-codemods&lt;/a&gt; I wrote tests to ensure changes for new features wouldn't break my previous work. As the tests grew in number, I found myself following a familiar pattern: refining tests and extracting boilerplate to further focus each test on the behavior we're testing (the test subject).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Jest extensions (custom serializers and matchers) below are available in the &lt;a href="https://www.npmjs.com/package/jest-postcss"&gt;&lt;code&gt;jest-postcss&lt;/code&gt;&lt;/a&gt; npm package.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Testing PostCSS Plugins
&lt;/h2&gt;

&lt;p&gt;While PostCSS plugins can have countless behaviors that need testing, they have a clear API to test - the input CSS and output CSS. We'll start with a single standalone test:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Result&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postcssScss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postcss-scss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should transform the css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeNestingSelector&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
      .rule { 
        &amp;amp;-part {}
      }
    &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postcssScss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CSS&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
    ".rule {
      } 
      .rule-part {}"
  &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note: I'm fond of inline snapshot tests, as long as they're concise.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Stripping out the PostCSS specifics, the test looks like this:&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="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should transform the css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;RECEIVED&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BOILERPLATE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SUBJECT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;INPUT&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RECEIVED&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;MATCHER&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EXPECTED&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;We can look at this test as having 2 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Apply the &lt;code&gt;BOILERPLATE&lt;/code&gt; function to the &lt;code&gt;SUBJECT&lt;/code&gt; plugin and &lt;code&gt;INPUT&lt;/code&gt; CSS, giving us the &lt;code&gt;RECEIVED&lt;/code&gt; CSS.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;RECEIVED&lt;/code&gt; against &lt;code&gt;EXPECTED&lt;/code&gt; using a &lt;code&gt;MATCHER&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  1. Extracting the Boilerplate
&lt;/h3&gt;

&lt;p&gt;Pulling out the &lt;code&gt;BOILERPLATE&lt;/code&gt; from our test case gives us the function &lt;code&gt;createProcessor&lt;/code&gt;:&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postcss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postcss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;postcssScss&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;postcss-scss&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;createProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plugins&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;configured&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&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;css&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;configured&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;postcssScss&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;CSS&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;We can now apply this function outside of the tests to avoid unnecessary setup for each test.&lt;/p&gt;

&lt;h3&gt;
  
  
  2a. Snapshot Serializers as &lt;code&gt;MATCHER&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If we use inline snapshots to compare &lt;code&gt;RECEIVED&lt;/code&gt; and &lt;code&gt;EXPECTED&lt;/code&gt;, we'll want to clean up the snapshot.&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="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
    ".rule {
      } 
      .rule-part {}"
&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The extra quotes and poor indentation distract from the goal of the test - to check that the &lt;code&gt;RECEIVED&lt;/code&gt; is the same as &lt;code&gt;EXPECTED&lt;/code&gt;. We can reformat the snapshot by adding a snapshot serializer to Jest with &lt;a href="https://jestjs.io/docs/en/expect#expectaddsnapshotserializerserializer"&gt;&lt;code&gt;expect.addSnapshotSerializer&lt;/code&gt;&lt;/a&gt;, prettifying the CSS for easy visual comparison.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;prettier&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prettier&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;serializeCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&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;prettier&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="c1"&gt;// keep empty rules compact for simpler testing&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\{\s&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\}&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;{}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&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;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addSnapshotSerializer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;print&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;serializeCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&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;Now any PostCSS &lt;code&gt;Result&lt;/code&gt; will render as prettified CSS when tested using Jest snapshots.&lt;/p&gt;

&lt;p&gt;After completing these two steps the test is much easier to read, making it easier to identify whether updates are intentional during code review. This refactor isn't worth it for a single test, but with 48 snapshot tests in &lt;code&gt;scss-codemods&lt;/code&gt;, the value adds up.&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createProcessor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;removeNestingSelector&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should fold out dash ampersand rules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
      .rule { 
        &amp;amp;-part1 {}
      }
    &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
    .rule {}
    .rule-part1 {}
  &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2b. Custom Matchers as &lt;code&gt;MATCHER&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As I mentioned before, I &lt;strong&gt;really&lt;/strong&gt; like snapshot tests, but sometimes you want to avoid test behavior automatically changing too easily with a simple command (&lt;code&gt;jest --update&lt;/code&gt;). We can write our own custom matcher using Jest's &lt;a href="https://jestjs.io/docs/en/expect#expectextendmatchers"&gt;expect.extend&lt;/a&gt; to achieve the same matching without the automatic update behavior of snapshot tests.&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="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;toMatchCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;css&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;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;serializeCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;css&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;received&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;serializeCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;css&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="na"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;received&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="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;matcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&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;isNot&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.not&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="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;.toMatchCSS&lt;/span&gt;&lt;span class="s2"&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matcherHint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;utils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;diff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;received&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;toMatchCSS&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The matcher function uses the same &lt;code&gt;serializeCSS&lt;/code&gt; function to format &lt;code&gt;RECEIVED&lt;/code&gt; and &lt;code&gt;EXPECTED&lt;/code&gt; CSS and Jest's &lt;a href="https://jestjs.io/docs/en/expect#thisutils"&gt;&lt;code&gt;this.utils&lt;/code&gt;&lt;/a&gt;, which provides helpers for writing matchers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;this.utils.matcherHint&lt;/code&gt; returns a string representing the failed test to help identify what failed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;this.utils.diff&lt;/code&gt; performs a string diff to identify the difference between the expected and received results.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can use the custom matcher in the same way as the inline snapshots.&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="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should fold out dash ampersand rules&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
      .rule { 
        &amp;amp;-part1 {}
      }
    &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toMatchCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
    .rule {}
    .rule-part1 {}
  &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An example of a failed test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;expect(received).toMatchCSS(expected)

- Expected
+ Received

- .rule {}
- .rule-part1 {}
+ .rule {
+   &amp;amp;-part1 {}
+ }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Snapshots vs. Matchers
&lt;/h2&gt;

&lt;p&gt;Using a snapshot or custom matcher is a personal choice, but here are some heuristics to help you decide.&lt;/p&gt;

&lt;p&gt;Snapshot tests are faster to write and work well as regression tests when you know your system already behaves well. They can update automatically, so they're well suited to rapidly changing behavior in tests as long as the snapshot is small enough to review.&lt;/p&gt;

&lt;p&gt;Custom matchers are more explicit and can support a more diverse set of checks. They work well when you want to confirm the behavior of a small part of the whole. Matchers also won't change without manual editing, so the risk of unintentional changes is lower.&lt;/p&gt;

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

&lt;p&gt;By extracting boilerplate and writing Jest extensions for PostCSS, we're able to simplify individual tests, focusing more on the test subject and expected behavior.&lt;/p&gt;

&lt;p&gt;PostCSS's clear API makes serializers and matchers the ideal tools for cleaning up these tests. Pulling these test extensions out of &lt;code&gt;scss-codemods&lt;/code&gt; and into &lt;code&gt;jest-postcss&lt;/code&gt; can help others write tests for their PostCSS plugins.&lt;/p&gt;

&lt;p&gt;I hope you enjoyed this post, and let me know in the comments how you're making Jest extensions work for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Making Jest Extensions Production-Ready
&lt;/h2&gt;

&lt;p&gt;This is a bonus section in case you're interested in publishing your own Jest extensions and need to write tests for them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Matchers
&lt;/h3&gt;

&lt;p&gt;Testing serializers and matchers is a little tricky. We are inverting the relationship of our tests - writing plugins to test matchers, instead of matchers to test plugins. For cases when &lt;code&gt;RECEIVED&lt;/code&gt; matches &lt;code&gt;EXPECTED&lt;/code&gt;, it's as simple as writing a test that passes, but we also need to ensure the matcher provides helpful hints when they don't match.&lt;/p&gt;

&lt;h3&gt;
  
  
  Error: Task Failed Successfully
&lt;/h3&gt;

&lt;p&gt;To test this behavior, we need to verify the error the matcher returns. Wrapping the failing &lt;code&gt;expect&lt;/code&gt; in a &lt;code&gt;expect(() =&amp;gt; {...}).rejects&lt;/code&gt; or a &lt;code&gt;try/catch&lt;/code&gt; block resolves this issue.&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;// We're testing a failure with an identity plugin  for simplicity&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createProcessor&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;postcssPlugin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;identity&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Once&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;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should fail with a helpful message&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
        .rule { 
          &amp;amp;-part1 {}
        }
      &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toMatchCSS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
      .rule {}
      .rule-part1 {}
    &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toMatchInlineSnapshot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="err"&gt;
    [Error: expect(received).toMatchCSS(expected)

    - Expected
    + Received

    - .rule {}
    - .rule-part1 {}
    + .rule {
    +   &amp;amp;-part1 {}
    + }]
  &lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test confirms the inner &lt;code&gt;expect&lt;/code&gt; throws an error matching the desired format, ensuring that the matcher provides helpful feedback to developers when tests using it fail.&lt;/p&gt;

</description>
      <category>postcss</category>
      <category>javascript</category>
      <category>codequality</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
