<?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: Ben Demboski</title>
    <description>The latest articles on DEV Community by Ben Demboski (@bendemboski).</description>
    <link>https://dev.to/bendemboski</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%2F227390%2F74abd2ba-c7bc-4024-801a-1c765eca2027.jpeg</url>
      <title>DEV Community: Ben Demboski</title>
      <link>https://dev.to/bendemboski</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bendemboski"/>
    <language>en</language>
    <item>
      <title>Upgrading to Ember 6.1: linting with type information</title>
      <dc:creator>Ben Demboski</dc:creator>
      <pubDate>Sat, 01 Feb 2025 04:25:49 +0000</pubDate>
      <link>https://dev.to/bendemboski/upgrading-to-ember-61-linting-with-type-information-2cke</link>
      <guid>https://dev.to/bendemboski/upgrading-to-ember-61-linting-with-type-information-2cke</guid>
      <description>&lt;p&gt;I recently upgraded all the packages in our very large monorepo to the Ember 6.1 app blueprint. The &lt;a href="https://github.com/ember-cli/ember-new-output/compare/v6.0.0...v6.1.0" rel="noopener noreferrer"&gt;pretty modest file-by-file diff&lt;/a&gt; that appears to just upgrade some dependencies and switch to the new &lt;code&gt;eslint&lt;/code&gt; flat config file format belies some very exciting and impactful changes to the code validations that &lt;code&gt;eslint&lt;/code&gt; performs.&lt;/p&gt;

&lt;p&gt;My pull request that upgrades to Ember 6.1 and updates all of our code to comply with the new default linting configuration included 13 commits containing changes to 12,100 lines of code across 1,155 files! This was virtually all driven by the change from &lt;code&gt;@typescript-eslint&lt;/code&gt;'s &lt;a href="https://github.com/ember-cli/ember-cli/blob/90d269b086dc3a1cc3c8b1e8d4451aa66f326c8a/blueprints/app/files/.eslintrc.js#L32" rel="noopener noreferrer"&gt;recommended&lt;/a&gt; configuration to its &lt;a href="https://github.com/ember-cli/ember-cli/blob/f4dcab6fcb3126190133f7b1e2870eb7dd1b0603/blueprints/app/files/_ts_eslint.config.mjs#L92" rel="noopener noreferrer"&gt;recommendedTypechecked&lt;/a&gt; configuration.&lt;/p&gt;

&lt;p&gt;I learned a lot in the process of updating all of our code to comply with the new linting configuration, and I wrote a fairly long and detailed PR description to share what I learned with my team, so I figured I'd share it with the broader community in case it's helpful for others. And here it is!&lt;/p&gt;




&lt;p&gt;Ember 6.1 includes some big linting updates, described below. So this PR updates us to Ember 6.1, including some miscellaneous/minor blueprint updates, but the bulk of it is making our codebase conform to the new linting rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  New linting setup
&lt;/h2&gt;

&lt;p&gt;One very notable change in the linting setup is that for &lt;code&gt;ts&lt;/code&gt; and &lt;code&gt;gts&lt;/code&gt; files, we've started using &lt;a href="https://typescript-eslint.io/getting-started/typed-linting/" rel="noopener noreferrer"&gt;linting with type information&lt;/a&gt;, which allows us to use linting rules that are much more powerful and helpful. The major implications of this are described below.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://typescript-eslint.io/rules/no-unused-vars/" rel="noopener noreferrer"&gt;@typescript-eslint/no-unused-vars&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This rule was updated to also flag unused variables in &lt;code&gt;catch&lt;/code&gt; statements, so where we used to do&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ignore&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 omit the variable declaration entirely and use a syntax I didn't even realize was allowed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;doSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ignore&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://typescript-eslint.io/rules/unbound-method/" rel="noopener noreferrer"&gt;@typescript-eslint/unbound-method&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This rule helps us make sure we don't call methods with &lt;code&gt;this&lt;/code&gt; bound incorrectly, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Thingy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;setObjValue&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="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thingy&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;setObjValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setObjValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// whoops, `this` will be `null`, so `this.obj.key`&lt;/span&gt;
&lt;span class="c1"&gt;// with throw an error&lt;/span&gt;
&lt;span class="nf"&gt;setObjValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When I was fixing up our code to comply with this rule, I encountered some "false positives" that I addressed in a few different ways, and are informative for writing code in the future to comply with this rule.&lt;/p&gt;

&lt;p&gt;The false positives mainly have to do with the fuzzy distinction between a class method and a property that happens to be a function. Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Thingy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;classMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;propertyThatIsAFunction&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="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Thingy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// unsafe because if `classMethod` references `this`, calling&lt;/span&gt;
&lt;span class="c1"&gt;// `classMethod()` won't have `this` bound correctly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classMethod&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// safe because arrow functions have `this` "pre-bound"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;propertyThatIsAFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;propertyThatIsAFunction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't just apply to classes, but also to objects, i.e. the above would be exactly the same with an object in place of the class &amp;amp; class instantiation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;classMethod&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="na"&gt;propertyThatIsAFunction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;someFunction&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;So, when this this rule flags something you are doing as an error, you can create an arrow function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classMethod&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="nx"&gt;thingy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;classMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or bind it when saving it off:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;classMethod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;classMethod&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thingy&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or if you have access to the original class and it seems to make sense, convert the method itself in the class definition to an inline arrow function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Thingy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;classMethod&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// or&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thingy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;classMethod&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;code&gt;any&lt;/code&gt;-related linting
&lt;/h2&gt;

&lt;p&gt;One of the risks of using values typed as &lt;code&gt;any&lt;/code&gt; is that they can "leak" out into a much bigger footprint of code than it seems like. For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Suppose `create-object` is typed so that `createObject()` returns&lt;/span&gt;
&lt;span class="c1"&gt;// `{ value: string }`&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createObject&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create-object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// fine&lt;/span&gt;
&lt;span class="nx"&gt;object&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="c1"&gt;// type error&lt;/span&gt;
&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subObject&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, if &lt;code&gt;create-object&lt;/code&gt; lacked type information,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @ts-expect-error no types&lt;/span&gt;
&lt;span class="c1"&gt;// createObject is typed as `any`&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createObject&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create-object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// `object` is typed as `any`&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// fine&lt;/span&gt;
&lt;span class="nx"&gt;object&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="c1"&gt;// also fine at compile-time, but will throw an error at runtime&lt;/span&gt;
&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subObject&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basically, as soon as you have a value typed as &lt;code&gt;any&lt;/code&gt;, any values you derive from it (via property accesses, function calls, etc etc) will be &lt;code&gt;any&lt;/code&gt; and the compiler won't be able to do any meaningful type checking on those values.&lt;/p&gt;

&lt;p&gt;We've had &lt;code&gt;@typescript-eslint/no-explicit-any&lt;/code&gt; enabled for quite some time, but this just prevents us from explicitly typing a value as &lt;code&gt;any&lt;/code&gt;. There are a bunch of cases where a value can be &lt;em&gt;implicitly&lt;/em&gt; typed as &lt;code&gt;any&lt;/code&gt;, and this rule is meant to "contain the damage."&lt;/p&gt;

&lt;p&gt;There a many many cases where this rule flags errors, and many ways of addressing those errors, but here are some common ones I ran into:&lt;/p&gt;

&lt;h4&gt;
  
  
  inherently untyped values
&lt;/h4&gt;

&lt;p&gt;When calling &lt;code&gt;JSON.parse()&lt;/code&gt; or &lt;code&gt;await fetchResponse.json()&lt;/code&gt; or the like, there is no static type information -- we, the programmer, have to tell the compiler what kind of value we expect to get from deserializing the JSON string into an object. So here, we just have to cast it, e.g. &lt;code&gt;let myResult = JSON.parse(str) as MyResult;&lt;/code&gt;. Sometimes the type isn't defined as a named type and it doesn't seem worthwhile to create a named type, so you can just do &lt;code&gt;let { value } = JSON.parse(str) as { value: string };&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  catch blocks
&lt;/h4&gt;

&lt;p&gt;In JavaScript, you can throw anything, not just &lt;code&gt;Error&lt;/code&gt; objects. &lt;code&gt;throw 'whoops'&lt;/code&gt; or even &lt;code&gt;throw undefined&lt;/code&gt; are perfectly valid (although bad practice, and we have a linting rule to prevent us from doing it in our code). So in catch blocks, even though we generally treat the thrown value as an &lt;code&gt;Error&lt;/code&gt;, we don't actually know what it is.&lt;/p&gt;

&lt;p&gt;Up until recently in our TypeScript configuration (where we had &lt;a href="https://www.typescriptlang.org/tsconfig/#useUnknownInCatchVariables" rel="noopener noreferrer"&gt;useUnknownInCatchVariables&lt;/a&gt; disabled), variables in catch blocks were typed as &lt;code&gt;any&lt;/code&gt; (mainly to ease the transition of code from un-typed JavaScript where we play fast and loose with our types to TypeScript). So this new rule flags pretty much any (hah!) usage of the caught variable as an error, and requires an explicit cast. NB, once this was done it wasn't much additional work to enable &lt;code&gt;useUnknownInCatchVariables&lt;/code&gt;, which I did, so now these variables in &lt;code&gt;catch&lt;/code&gt; blocks are typed as &lt;code&gt;unknown&lt;/code&gt; and the compiler (as opposed to the linter) won't let us do anything with them without casting/type-narrowing.&lt;/p&gt;

&lt;p&gt;To be as risk-averse as possible, we should ideally always use &lt;code&gt;instanceof&lt;/code&gt; checks or type guards or whatever to ensure that the thrown thing is of a type that is compatible with what we expect, e.g.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;doThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;reportErrorMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&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="nf"&gt;reportErrorMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;reportErrorMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;unknown error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is a big pain, and given that we lint against throwing non-Error objects for our code, it should generally be an edge case, so in cases like the above I pretty much always did something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;doThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;reportErrorMessage&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;message&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;and I think that's fine for us to do in general unless we're in an area of the code where we're particularly worried about what might be thrown (e.g. from external code) or want to be extra defensive.&lt;/p&gt;

&lt;h4&gt;
  
  
  error-typed and poorly-typed values
&lt;/h4&gt;

&lt;p&gt;By "error-typed values" I mean ones where we use &lt;code&gt;@ts-expect-error&lt;/code&gt; to tell the compiler to not worry about the fact that we're doing something it doesn't understand, and then it types any values that come out of that statement as &lt;code&gt;any&lt;/code&gt;. By poorly-typed values I mean places where we intentionally use "any" such as the &lt;code&gt;context&lt;/code&gt; in the &lt;code&gt;ModalManager&lt;/code&gt; (typically because we haven't yet taken the effort to figure out how to type it correctly).&lt;/p&gt;

&lt;p&gt;In both of these cases, if you can't fix the underlying problem, just cast. For example,&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @ts-expect-error no types&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createObject&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create-object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createObject&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="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or even (although the former is probably preferable in most cases)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// @ts-expect-error no types&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;createObject&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;create-object&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// leave it typed as `any`&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createObject&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// we don't care about `key` here, so don't bother to include it&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  promise/async-related linting
&lt;/h2&gt;

&lt;p&gt;We now have linting rules that verify a bunch of things related the async/await and promises, and force us to be more thoughtful and intentional about our async/promise handling, which produces safer code. The two most significant rules are:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://typescript-eslint.io/rules/require-await/" rel="noopener noreferrer"&gt;@typescript-eslint/require-await&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This rule requires that &lt;code&gt;async&lt;/code&gt; functions either have an &lt;code&gt;await&lt;/code&gt; statement in them, or explicitly return a promise. When this rule flags an error, you probably want to just remove the &lt;code&gt;async&lt;/code&gt; keyword from the function, making it synchronous. However, in some cases you might be passing the function to an API that requires a promise-returning function, so just removing the &lt;code&gt;async&lt;/code&gt; keyword produces a type error. In this case you can leave the &lt;code&gt;async&lt;/code&gt; and wrap any return values in &lt;code&gt;Promise.resolve()&lt;/code&gt; e.g. &lt;code&gt;return Promise.resolve('result')&lt;/code&gt; or just &lt;code&gt;return Promise.resolve()&lt;/code&gt; if the function returns &lt;code&gt;void&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://typescript-eslint.io/rules/no-floating-promises/" rel="noopener noreferrer"&gt;@typescript-eslint/no-floating-promises&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;This rule prevents us from "ignoring" promises. For example, if you call an &lt;code&gt;async&lt;/code&gt; function, you have to do one of the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// await the returned promise&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;asyncThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// store the promise in a variable&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promise&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;asyncThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// pass the promise to a function&lt;/span&gt;
&lt;span class="nf"&gt;handlePromise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;asyncThing&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="c1"&gt;// apply a `.catch()` handler to it&lt;/span&gt;
&lt;span class="nf"&gt;asyncThing&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// explicitly ignore it with the `void` operator&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;asyncThing&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 meant to ensure that we don't accidentally discard promises that we should be handling, both to prevent async/race condition/timing errors, and to make sure that if the promise rejects, it isn't an unhandled rejection (which can cause us to miss errors or, in Node, to cause the process to exit).&lt;/p&gt;

&lt;p&gt;This rule forces us to think more carefully about our use of asynchronous functions, and whether they are actually asynchronous at the API level, or whether they just have asynchronous behavior as an internal implementation detail. Consider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getFromServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setAndTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// get some stats from the object (async operation) and send them to a metrics service&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stats&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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendTracksToMetricsService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&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;In the first case, the caller would &lt;code&gt;await&lt;/code&gt; the function call, and the async-ness is actually a part of the function's API -- getting from the server is inherently asynchronous and the caller needs to wait for the result. In the second case, the caller would not &lt;code&gt;await&lt;/code&gt; the function call because metrics tracking is a side-effect and the fact that it's asynchronous is an implementation detail. So the second function's API is synchronous, we are just making the function &lt;code&gt;async&lt;/code&gt; so we get the convenient &lt;code&gt;await&lt;/code&gt; syntax, rather than having to use promise chaining:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setBroadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// get some stats from the object (async operation) and send them to a metrics service&lt;/span&gt;
  &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStats&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;sendTracksToMetricsService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* handle error */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, if we had the original &lt;code&gt;async&lt;/code&gt; implementation of &lt;code&gt;setBroadcast&lt;/code&gt;, it would be correct to call it without &lt;code&gt;await&lt;/code&gt;ing it because the caller doesn't want to wait for the metrics tracking to complete before proceeding. But this would trigger a linting error. In this case, it's telling us that the function's API should really be synchronous, and we should rewrite the function to not be &lt;code&gt;async&lt;/code&gt; (rather than having all the call sites add a &lt;code&gt;void&lt;/code&gt; or &lt;code&gt;catch()&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;In this case, we can have our cake and eat it too using an async &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/IIFE" rel="noopener noreferrer"&gt;IIFE&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setBroadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Thing&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;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="c1"&gt;// get some stats from the object (async operation) and send them to a metrics service&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;stats&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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getStats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendTracksToMetricsService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="cm"&gt;/* handle error */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In cases where we truly do want to invoke an async function but not wait for it to complete before proceeding, it's probably best to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;doAsyncThing&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;captureException&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or in very rare cases where we're pretty darn confident it's not going to throw an error&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;doReallySafeAsyncThing&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ember</category>
      <category>typescript</category>
      <category>eslint</category>
    </item>
    <item>
      <title>Embroider: from zero to route splitting in 3.5 weeks</title>
      <dc:creator>Ben Demboski</dc:creator>
      <pubDate>Wed, 19 May 2021 06:07:56 +0000</pubDate>
      <link>https://dev.to/bendemboski/embroider-from-zero-to-route-splitting-in-3-5-weeks-5abo</link>
      <guid>https://dev.to/bendemboski/embroider-from-zero-to-route-splitting-in-3-5-weeks-5abo</guid>
      <description>&lt;p&gt;I spent the last 3.5 weeks or so switching our primary app over to using &lt;a href="https://github.com/embroider-build/embroider"&gt;embroider&lt;/a&gt;, and getting it working with all the optimized settings plus code splitting across routes. Hopefully reading about my experience will help others through the same process, and help accelerate polishing and adoption of embroider within the Ember ecosystem.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
Background

&lt;ul&gt;
&lt;li&gt;Our application&lt;/li&gt;
&lt;li&gt;Goals&lt;/li&gt;
&lt;li&gt;Pre-embroider solutions&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Our experience moving to embroider

&lt;ul&gt;
&lt;li&gt;Overall impressions&lt;/li&gt;
&lt;li&gt;Process&lt;/li&gt;
&lt;li&gt;What was hard&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Other tips, tricks, and gotchas

&lt;ul&gt;
&lt;li&gt;Addon build failures&lt;/li&gt;
&lt;li&gt;Out-of-memory errors in CI&lt;/li&gt;
&lt;li&gt;Addon-supplied webpack configuration&lt;/li&gt;
&lt;li&gt;Rebuild on addon changes&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Conclusions&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Background
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Our application
&lt;/h2&gt;

&lt;p&gt;I work at &lt;a href="https://rowanpatents.com/"&gt;Rowan Patents&lt;/a&gt;, and our main Ember app is a desktop (&lt;a href="https://ember-electron.js.org/"&gt;ember-electron&lt;/a&gt;) application for patent attorneys to use to draft patent applications -- somewhat analogous to an IDE like VSCode. Most patents consist of a bunch of pages of prose, and some pages of drawings, so most patent attorneys use Microsoft Word and Visio for their patent drafting.&lt;/p&gt;

&lt;p&gt;Rowan Patents brings together the prose and drawings, along with a number of other efficiency tools, into a single multi-window desktop application. When an attorney opens a patent application file, they will have their primary prose window, and then also might simultaneously open a drawings window and a tools window (and there are several other special-purpose windows like a splash screen and a landing page). This means that there's a pretty strong association between routes and windows -- the prose window will never visit any of the drawings routes, and vice versa -- and also that we typically have several copies of the application loaded into memory at a time (one per window).&lt;/p&gt;

&lt;p&gt;We also integrate quite a few third-party libraries for our core editor technology, for our core diagramming technology, and various other data analysis and transformations such as natural language processing. We've found that many of these bundle a lot of extra code that we don't actually execute or need.&lt;/p&gt;

&lt;p&gt;Statistics on our app (line counts done using &lt;a href="https://github.com/AlDanial/cloc"&gt;cloc&lt;/a&gt;):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;11 addons&lt;/li&gt;
&lt;li&gt;~55,000 lines of Ember js/ts code&lt;/li&gt;
&lt;li&gt;~9,500 lines of hbs&lt;/li&gt;
&lt;li&gt;~3,500 lines of scss&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Goals
&lt;/h2&gt;

&lt;p&gt;Our two primary goals in moving to embroider were to be able to take advantage of tree-shaking and code splitting across routes, to keep our code-size-related memory footprint under control without incurring high up-front engineering costs or ongoing maintenance costs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pre-embroider solutions
&lt;/h2&gt;

&lt;p&gt;Before moving to embroider, we were able to do quite a bit of "manual tree-shaking" of third-party non-Ember libraries. Using module aliasing, &lt;code&gt;null-loader&lt;/code&gt;, and &lt;code&gt;string-replace-loader&lt;/code&gt; in the webpack config that we supplied to &lt;code&gt;ember-auto-import&lt;/code&gt;, we were able to strip out a lot of unneeded code, but at the cost of significant up-front effort and maintenance cost as we upgrade those third-party libraries.&lt;/p&gt;

&lt;p&gt;We did not have a solution for splitting our code across routes. &lt;br&gt;
We considered &lt;a href="https://ember-engines.com/"&gt;Ember Engines&lt;/a&gt;, but opted to wait for embroider to mature...which it has!&lt;/p&gt;
&lt;h1&gt;
  
  
  Our experience moving to embroider
&lt;/h1&gt;

&lt;p&gt;Note: all of this was using embroider v0.40.0.&lt;/p&gt;
&lt;h2&gt;
  
  
  Overall impressions
&lt;/h2&gt;

&lt;p&gt;Based on my overall experience, I would qualify embroider as late-stage alpha or early-stage beta quality software. The core functionality is there and is solid, but I still ran across several fairly severe bugs, and the documentation is limited. I spent a good deal of time debugging through embroider's code to determine why certain aspects of my build weren't working, and I don't think I would have successfully executed the whole transition to embroider if I had been unwilling to roll up my sleeves and dive in like that. I don't think the same is necessarily true of smaller apps with fewer dependencies though.&lt;/p&gt;

&lt;p&gt;However, after getting our application building, and fixing some runtime errors, it's been feeling stable, and I'm not feeling nervous about inconsistencies in production build output, frequent but intermittent oddities in the development environment, or any of those other things that would otherwise be keeping me up at night.&lt;/p&gt;
&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;My process for getting our app/addons building on embroider with optimized settings was:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Get all addons building on embroider&lt;/li&gt;
&lt;li&gt;Get the app building on embroider&lt;/li&gt;
&lt;li&gt;Manually test &amp;amp; stabilize the app&lt;/li&gt;
&lt;li&gt;Merge back to &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Get all addons building with optimized flags&lt;/li&gt;
&lt;li&gt;Get the app building with optimized flags&lt;/li&gt;
&lt;li&gt;Enable route code splitting&lt;/li&gt;
&lt;li&gt;Manually test &amp;amp; stabilize the app&lt;/li&gt;
&lt;li&gt;Merge back to &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Celebrate, spend a chill weekend backpacking on the Washington Coast, and then write this article&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  What was hard
&lt;/h2&gt;

&lt;p&gt;I've tried to break down all the work I put into making the app build on embroider into categories, and I'll describe them generally without diving too deep. Hopefully they make some sense and it's useful -- I promise nothing!&lt;/p&gt;
&lt;h3&gt;
  
  
  ES6 module compliance
&lt;/h3&gt;

&lt;p&gt;One thing we had to update in a number of places was code that imported a module as if it had a default export when really it only had named exports. For example, given a module named &lt;code&gt;lib&lt;/code&gt; with two named exports, &lt;code&gt;foo&lt;/code&gt; and &lt;code&gt;bar&lt;/code&gt; (and no default export), we would sometimes do 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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would work with Ember's module loader, but is not ES6-compliant, and will not work with embroider/webpack. We had to either update to:&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="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;lib&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;lib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or ideally (because it's better for tree-shaking):&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another related issue had to do with export stubbing. We had a relatively common pattern in our tests of using &lt;a href="https://sinonjs.org/"&gt;sinon&lt;/a&gt; to stub or spy on module exports. Using the above &lt;code&gt;lib&lt;/code&gt; example, we might have a test that would do:&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;let&lt;/span&gt; &lt;span class="nx"&gt;fooStub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sinon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stub&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;fooStub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;returns&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.button-that-calls-lib-foo&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fooStub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callCount&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;window.require()&lt;/code&gt; would use &lt;a href="https://github.com/ember-cli/loader.js/"&gt;Ember's module loader&lt;/a&gt; to load the &lt;code&gt;foo&lt;/code&gt; module, and then stub out one of its exports. This will not work with webpack-generated modules for a couple of reasons, so we had to find other patterns, usually involving either doing the stubbing at a different point in the call stack, or instrumenting the logic in the code itself to provide a stubbing mechanism that wasn't re-writing the ES6 module objects themselves.&lt;/p&gt;

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

&lt;p&gt;Unfortunately I don't have a lot of information to share on this one because I never fully bottomed out my understanding, just treated symptoms. But I'll describe what I do know.&lt;/p&gt;

&lt;p&gt;We had a number of build failures related to transitive dependencies. For example, we use &lt;code&gt;ember-power-select&lt;/code&gt;, which depends on &lt;code&gt;ember-basic-dropdown&lt;/code&gt;, and also use &lt;code&gt;ember-basic-dropdown&lt;/code&gt; directly. We did not have &lt;code&gt;ember-basic-dropdown&lt;/code&gt; declared in our &lt;code&gt;package.json&lt;/code&gt;, so when &lt;code&gt;@embroider/compat&lt;/code&gt; assembled our app into the v2 package format, it would not make &lt;code&gt;ember-basic-dropdown&lt;/code&gt; accessible to our app (basically it would put it in &lt;code&gt;node_modules/ember-power-select/node_modules/ember-basic-dropdown&lt;/code&gt; instead of &lt;code&gt;node_modules/ember-basic-dropdown&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;I believe in some cases we ran into errors where the dependency was only listed as a &lt;code&gt;peerDependency&lt;/code&gt; and needed to be a &lt;code&gt;dependency&lt;/code&gt; or &lt;code&gt;devDependency&lt;/code&gt;, although perhaps that was non-addon libraries, I don't recall and unfortunately didn't leave enough of a paper trail to verify.&lt;/p&gt;

&lt;p&gt;But overall, given how embroider compiles apps/addons into a more statically analyzable and broadly standards compliant package format, we had to do a fair amount of hardening how we declare our dependencies in our various &lt;code&gt;package.json&lt;/code&gt;s.&lt;/p&gt;

&lt;h3&gt;
  
  
  Statically analyzable components
&lt;/h3&gt;

&lt;p&gt;This already has a &lt;a href="https://github.com/embroider-build/embroider/blob/master/REPLACING-COMPONENT-HELPER.md"&gt;great writeup&lt;/a&gt; in the embroider documentation. We weren't doing anything truly dynamic, only using dynamic-looking components as a way of currying components' arguments multiple times, so it was not very difficult for us to refactor to avoid doing that, although in retrospect I think we might have been able to more easily do it using the not-yet-documented (outside code comments, as far as I can tell) &lt;a href="https://github.com/embroider-build/embroider/blob/12f06616713645b4f0696b27c5d673562f68dcb8/packages/compat/src/dependency-rules.ts#L6"&gt;packageRules&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS/SASS
&lt;/h3&gt;

&lt;p&gt;I spent a good deal of effort getting our SCSS (via &lt;code&gt;ember-cli-sass&lt;/code&gt;) and CSS working under embroider. Unfortunately I don't have great detail to offer here, because due to Reasons™ (and probably not great ones), I ended up switching from &lt;code&gt;node-sass&lt;/code&gt; to &lt;code&gt;dart-sass&lt;/code&gt; as part of the move to embroider, so I'm not actually certain how much fiddling was needed for embroider, and how much for &lt;code&gt;dart-sass&lt;/code&gt;. But I am certain that it was a mix of the two, so be warned that there may be some non-trivial effort involved in getting your styles working when moving to embroider.&lt;/p&gt;

&lt;h3&gt;
  
  
  Third-party addons
&lt;/h3&gt;

&lt;p&gt;Hooboy, this was definitely a biiiiig hunk of the work. Our app has been under development for 6ish years, so it has a whole history of legacy code relying on older addons that aren't really Octane-y, plus several modern addons that haven't yet been brought up to embroider compatibility, let alone embroider optimized compatibility.&lt;/p&gt;

&lt;p&gt;Getting all the third-party addons working took a lot of effort, but fortunately webpack provides a lot of configuration and tools to hack on things and make them work. Here is a list of addons that were problematic (that should shrink over time) and what we did about them -- I hope these solutions will help people with identical problems and/or serve as examples to help address issues with addons we do not use.&lt;/p&gt;

&lt;p&gt;I should also note that there are undoubtedly better ways of doing some of this -- I often took a brute force webpack approach because that's what I knew, but I believe embroider's &lt;a href="https://github.com/embroider-build/embroider/blob/12f06616713645b4f0696b27c5d673562f68dcb8/packages/compat/src/dependency-rules.ts#L6"&gt;packageRules&lt;/a&gt; might provide nicer solutions to some of these issues.&lt;/p&gt;

&lt;p&gt;Before trying any workarounds, I would always update the problematic addon(s) to their latest version, so all of these descriptions and workarounds apply to latest published versions as of 5/14/2021.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://adopted-ember-addons.github.io/ember-file-upload/"&gt;ember-file-upload&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ember-file-upload&lt;/code&gt; imports code from &lt;code&gt;ember-cli-mirage&lt;/code&gt; that might not actually be included in the build -- see the discussion in &lt;a href="https://github.com/adopted-ember-addons/ember-file-upload/issues/425"&gt;this&lt;/a&gt; issue for more details. Since we don't use &lt;code&gt;ember-file-upload&lt;/code&gt;'s mirage utilities, we worked around this by stubbing out the import:&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;emberFileUploadMiragePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-file-upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mirage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// &amp;lt;snip&amp;gt;&lt;/span&gt;

&lt;span class="nl"&gt;webpackConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;alias&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;emberFileUploadMiragePath&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;a href="https://github.com/cibernox/ember-power-select"&gt;ember-power-select&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;Despite having some code in &lt;code&gt;@embroider/compat&lt;/code&gt; to support this addon, I found that it would not work with the embroider optimized flags all turned on, I think because even if embroider knows what &lt;code&gt;@someComponent&lt;/code&gt; is, the &lt;code&gt;or&lt;/code&gt; in &lt;code&gt;{{component (or @someComponent "default-component")}}&lt;/code&gt; messes up its static analysis. So, I got my friend (that we'll see a &lt;em&gt;lot&lt;/em&gt; more of), &lt;a href="https://github.com/Va1/string-replace-loader"&gt;string-replace-loader&lt;/a&gt; to help out and came up with &lt;a href="https://gist.github.com/bendemboski/907b8a53040213a911301ad48172b99b"&gt;this&lt;/a&gt; wonderful bit of webpack config that definitely doesn't make me secretly feel dirty and/or naughty.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="http://ember-concurrency.com/"&gt;ember-concurrency&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;With embroider optimized flags (&lt;code&gt;staticAddonTrees&lt;/code&gt; in particular), production builds of &lt;code&gt;ember-concurrency&lt;/code&gt; fail with a &lt;a href="https://github.com/embroider-build/embroider/issues/811"&gt;bizarre error&lt;/a&gt;. The current theory is that it's a webpack bug, but fortunately the &lt;a href="https://github.com/embroider-build/embroider/issues/811#issuecomment-840180944"&gt;workaround&lt;/a&gt; is pretty straightforward.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="http://ember-cli-page-object.js.org/"&gt;ember-cli-page-object&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ember-cli-page-object&lt;/code&gt; has a &lt;a href="https://github.com/san650/ember-cli-page-object/blob/16007bbaedbe9fc2f9c10e45ab05150c67d32bbf/addon-test-support/-private/compatibility.js"&gt;compatibility module&lt;/a&gt; that is meant to support older setups with old versions of &lt;code&gt;@ember/test-helpers&lt;/code&gt;, or even from before that library existed. It exposes an API that is meant to more or less emulate &lt;code&gt;@ember/test-helpers&lt;/code&gt;, and internally does some tricks with testing for modules at runtime that does not work with embroider optimized.&lt;/p&gt;

&lt;p&gt;Since we know our code always has &lt;code&gt;@ember/test-helpers&lt;/code&gt; present, we can use &lt;a href="https://github.com/Va1/string-replace-loader"&gt;string-replace-loader&lt;/a&gt; to make that compatibility module &lt;a href="https://gist.github.com/bendemboski/05d00b3f95e7ce8b720b22d161fa5d28"&gt;statically import and re-export&lt;/a&gt; &lt;code&gt;@ember/test-helpers&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://github.com/adopted-ember-addons/ember-metrics"&gt;ember-metrics&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ember-metrics&lt;/code&gt; relies on some features of Ember's container and module loader that do not work under embroider optimized to dynamically look up its metrics adapters based on names specified in &lt;code&gt;config/environment.js&lt;/code&gt;. Since I know the static list of adapters that our application uses, I wrote an initializer to statically import those adapters and then register them in the container using the names under which &lt;code&gt;ember-metrics&lt;/code&gt; expects to find them:&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;intercomAdapter&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-metrics/metrics-adapters/intercom&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;mixpanelAdapter&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-metrics/metrics-adapters/mixpanel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metrics-adapter:intercom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;intercomAdapter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;metrics-adapter:mixpanel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mixpanelAdapter&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;a href="https://github.com/poteto/ember-changeset-validations"&gt;ember-changeset-validations&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;The whole &lt;code&gt;ember-changeset&lt;/code&gt;/&lt;code&gt;ember-changeset-validations&lt;/code&gt;/&lt;code&gt;ember-validators&lt;/code&gt; bundle did not have dependencies set up and declared in a way that would work with embroider optimized, so some module aliasing and an appearance by my new best friend, &lt;a href="https://github.com/Va1/string-replace-loader"&gt;string-replace-loader&lt;/a&gt; provided a &lt;a href="https://gist.github.com/bendemboski/9a498a6eda26e102597d98e5b7718c9c"&gt;workaround&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;a href="https://adfinis-sygroup.github.io/ember-validated-form/"&gt;ember-validated-form&lt;/a&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;ember-validated-form&lt;/code&gt; does a &lt;em&gt;lot&lt;/em&gt; of dynamic component invocations that embroider cannot statically analyze. I believe the fact that it uses curly component syntax and doesn't use &lt;code&gt;this.foo&lt;/code&gt; vs &lt;code&gt;@foo&lt;/code&gt; makes it even harder on embroider. The &lt;a href="https://gist.github.com/bendemboski/962fd6afd1e425c8e47460e370ae4153"&gt;workaround&lt;/a&gt; involved soooooo much &lt;a href="https://github.com/Va1/string-replace-loader"&gt;string-replace-loader&lt;/a&gt;, but it works!&lt;/p&gt;

&lt;h3&gt;
  
  
  Route-splitting miscellanea
&lt;/h3&gt;

&lt;p&gt;At the end of this all, enabling route-splitting was a very pleasant anti-climax. It &lt;em&gt;almost&lt;/em&gt; just worked, and I was SO EXCITED to see only the code I expected/wanted loaded in each window of our application. The couple of minor issues I had to address when enabling route splitting were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We had one route whose dynamic parameter was &lt;code&gt;:id&lt;/code&gt; and even though it didn't need to implement the &lt;code&gt;serialize()&lt;/code&gt; hook (which is &lt;a href="https://github.com/embroider-build/embroider/blob/75344d894160e5f6b1951c5de89195a393394a46/packages/router/README.md#compatibility"&gt;warned about&lt;/a&gt; in the documentation), when using the embroider router, passing a model instance to &lt;a href="https://api.emberjs.com/ember/3.26/classes/RouterService/methods/get?anchor=urlFor"&gt;RouterService.urlFor()&lt;/a&gt; did not work. Evidently the code wanted the dynamic parameter to end with &lt;code&gt;_id&lt;/code&gt; before it would look for an &lt;code&gt;id&lt;/code&gt; property on the model instance, so I changed the dynamic parameter to &lt;code&gt;:sheet_id&lt;/code&gt; and everything was hunky dory&lt;/li&gt;
&lt;li&gt;We had one test that was doing &lt;code&gt;this.owner.lookup('controller:invention')&lt;/code&gt; before calling &lt;code&gt;visit()&lt;/code&gt;, and that was not returning the controller instance. I believe this was because that controller/route were lazy-loaded, so the code wasn't loaded and the factory wasn't registered with the container before &lt;code&gt;visit()&lt;/code&gt;. I fixed this by shuffling the test code around to not do the &lt;code&gt;lookup()&lt;/code&gt; until after the &lt;code&gt;visit()&lt;/code&gt; call, but this one has me kinda nervous that I have a lot of other &lt;code&gt;lookup()&lt;/code&gt;s like this that are only succeeding because they run after tests that have already caused the code for the lazy-loaded routes to load...but I haven't confirmed/investigated this any further yet.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Other tips, tricks, and gotchas
&lt;/h1&gt;

&lt;p&gt;Here I've collected some gotchas that I ran into and overcame, and some tips and tricks for smoothing out the whole setup and developer experience of working in this bravely-embroidered new world.&lt;/p&gt;

&lt;h2&gt;
  
  
  Addon build failures
&lt;/h2&gt;

&lt;p&gt;There is currently a &lt;a href="https://github.com/embroider-build/embroider/issues/799"&gt;bug&lt;/a&gt; in embroider that causes addon builds to non-deterministically produce incorrect output so that tests and the dummy app will not load or run. Until it is fixed, the workaround is to disable concurrency (set &lt;code&gt;JOBS=1&lt;/code&gt;) in your environment anytime you're building addons (this does not have any effect on apps).&lt;/p&gt;

&lt;p&gt;Because this not only produces a broken build but also poisons the embroider cache, I've applied a draconian workaround to all of our addons:&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;// ember-cli-build.js&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&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;EmberAddon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ember-cli/lib/broccoli/ember-addon&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Webpack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@embroider/webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaults&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;app&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;EmberAddon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;defaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Add options here&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="c1"&gt;// https://github.com/embroider-build/embroider/issues/799&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JOBS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@embroider/compat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;compatBuild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Webpack&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;h2&gt;
  
  
  Out-of-memory errors in CI
&lt;/h2&gt;

&lt;p&gt;Until &lt;a href="https://github.com/embroider-build/embroider/pull/795"&gt;this fix&lt;/a&gt; is released (which should happen in the next release after v0.40.0), embroider doesn't quite fully respect &lt;code&gt;JOBS=1&lt;/code&gt;. With &lt;code&gt;JOBS=1&lt;/code&gt; it will run the entire build in a single process, but it still warms a worker pool whose size is number-of-cpus-minus-one which, in CI, can be a very big number. Spawning all of these processes and having them load up their code (even though they don't do anything) consumed enough memory that in some of our CI scenarios, there was not enough left over for our build/tests/etc.&lt;/p&gt;

&lt;p&gt;I don't know of a good workaround. My super ugly one was to compile &lt;code&gt;@embroider/webpack&lt;/code&gt;'s latest master, check in a copy of &lt;code&gt;ember-webpack.js&lt;/code&gt; to my source tree, and then manually copy it into &lt;code&gt;node_modules&lt;/code&gt; to overwrite the existing v0.40.0 file at the beginning of the CI run. DON'T LOOK AT ME!!!!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Addon-supplied webpack configuration
&lt;/h2&gt;

&lt;p&gt;When using &lt;code&gt;ember-auto-import&lt;/code&gt; pre-embroider, addons could supply a &lt;a href="https://github.com/ef4/ember-auto-import#customizing-build-behavior"&gt;configuration&lt;/a&gt; to use when building their assets, including a webpack configuration. This could be very handy when addons included third-party libraries that they themselves added to the application bundle using &lt;code&gt;ember-auto-import&lt;/code&gt;. As far as I can discern, embroider doesn't have a similar mechanism.&lt;/p&gt;

&lt;p&gt;I think the whole idea of modularizing and then merging webpack configs is a little fraught because webpack wasn't really designed for modular configurations -- it all gets merged into a single configuration, so it's not hard to concoct scenarios where the different "modules" of configuration conflict and mess each other up.&lt;/p&gt;

&lt;p&gt;That being said, in controlled environments like ours where the addons are private to our applications, and we have specific knowledge of how/where they will be used, such a mechanism can be very handy. So we ended up standardizing on addons putting their webpack configuration in &lt;code&gt;/webpack.config.js&lt;/code&gt; so they could be imported and merged from consuming apps/addons.&lt;/p&gt;

&lt;p&gt;For example, suppose we have an app, creatively called &lt;code&gt;app&lt;/code&gt; that depends on &lt;code&gt;addon-a&lt;/code&gt; and &lt;code&gt;addon-b&lt;/code&gt;. &lt;a href="https://gist.github.com/bendemboski/d258676f6807e4c81fd7841ca346b840"&gt;This gist&lt;/a&gt; shows how we might put together and consume reusable webpack configs for this scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebuild on addon changes
&lt;/h2&gt;

&lt;p&gt;Pre-embroider, if an addon's &lt;code&gt;index.js&lt;/code&gt; implemented &lt;code&gt;isDevelopingAddon()&lt;/code&gt; to return &lt;code&gt;true&lt;/code&gt;, then when running &lt;code&gt;ember serve&lt;/code&gt; or &lt;code&gt;ember test -s&lt;/code&gt;, the build watcher would watch that addon and auto-rebuild/live-reload when any changes were made to that addon. embroider uses a different mechanism, which is the environment variable &lt;code&gt;EMBROIDER_REBUILD_ADDONS&lt;/code&gt; that can contain a comma-separated list of the names of addons to watch.&lt;/p&gt;

&lt;p&gt;I like this mechanism much better because I don't have to modify code to change which addons are and aren't build-watched. But in the specific case of our monorepo with our app and addons, I &lt;em&gt;always&lt;/em&gt; want to build-watch the in-monorepo addons. This is because our addons are not published, and are really only separate from the app for code organization and dev/test efficiency reasons. I could set &lt;code&gt;EMBROIDER_REBUILD_ADDONS&lt;/code&gt; to those addons in my login configs or something, but I wanted a zero-config solution that would work for my whole team. So I settled on doing this little trick in all our in-monorepo addons' &lt;code&gt;index.js&lt;/code&gt; files:&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// embroider doesn't respect isDevelopingAddon(), and we don't publish this&lt;/span&gt;
&lt;span class="c1"&gt;// addon, so always add ourselves to the list of auto-rebuild addons&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;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EMBROIDER_REBUILD_ADDONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&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;Set&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EMBROIDER_REBUILD_ADDONS&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./package&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;name&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;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way anytime an app or addon runs a build that depends on this addon, it will add itself to the list of rebuild addons.&lt;/p&gt;

&lt;p&gt;PLEASE DO NOT DO THIS IN PUBLISHED ADDONS!!!&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusions
&lt;/h1&gt;

&lt;p&gt;At the end of this all, we had what we wanted (tree-shaking and code splitting across routes), and I was very excited. It was a good deal of work (3.5 weeks or so as my main focus), although a lot of that was taken up by investigations of embroider bugs, followed by bug reports, sometimes contributing fixes, and sometimes finding the workarounds described above. So hopefully that will pave the way for a faster/smoother experience for others.&lt;/p&gt;

&lt;p&gt;embroider is moving fast, and I'm sure much of this article will be obsolete before long, but I hope it does provide some value in the meantime. I'd also love to hear from others -- your experiences with embroider, ideas to improve on the suggestions and methods I've provided, core team members telling me how I've missed the boat and misrepresented anything/suggested anything silly, etc. I hang out in the #dev-embroider channel on Ember Discord, and will happily discuss/answer questions/etc. here or there.&lt;/p&gt;

</description>
      <category>ember</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Simplifying the git forking workflow</title>
      <dc:creator>Ben Demboski</dc:creator>
      <pubDate>Tue, 13 Apr 2021 18:01:36 +0000</pubDate>
      <link>https://dev.to/bendemboski/simplifying-the-git-forking-workflow-4bkf</link>
      <guid>https://dev.to/bendemboski/simplifying-the-git-forking-workflow-4bkf</guid>
      <description>&lt;p&gt;The standard way to contribute to an open source project that you do not maintain is to fork it, create a branch in your fork where you put your code changes, and then open a pull request into the original repository. I've been doing this for years, and just discovered a tweak to the workflow that I really like and want to share with you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The git forking workflow
&lt;/h2&gt;

&lt;p&gt;This is a well established pattern that has been written up a number of times (e.g. &lt;a href="https://gist.github.com/Chaser324/ce0505fbed06b947d962"&gt;here&lt;/a&gt; and &lt;a href="https://www.atlassian.com/git/tutorials/comparing-workflows/forking-workflow"&gt;here&lt;/a&gt;), so I'll just briefly outline the process of opening a pull request, and then opening a second pull request, as I originally learned it.&lt;/p&gt;

&lt;h3&gt;
  
  
  First pull request
&lt;/h3&gt;

&lt;p&gt;To open my first pull request, I need to first create a fork. I'll use the Ember test helpers repo as my example that I'm contributing to. Note that none of this is specific to GitHub -- it would work the same with BitBucket or any other git host. My steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork the repo, so the original is at &lt;code&gt;git+ssh://git@github.com/emberjs/ember-test-helpers&lt;/code&gt; and my fork is at &lt;code&gt;git+ssh://git@github.com/bendemboski/ember-test-helpers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git clone git+ssh://git@github.com/bendemboski/ember-test-helpers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout -b my-branch-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push -u origin my-branch-1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;and now I'm ready to open a pull request! This is nice and simple, but where I think it gets a little complicated is when I want to make my second pull request.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second pull request
&lt;/h3&gt;

&lt;p&gt;If some time has passed, Ember test helpers' &lt;code&gt;master&lt;/code&gt; branch will have changed since I created my fork as work on the project continues -- at the very least, I hope my pull request was merged into it! So when creating my new branch, I need to make sure to branch off of the latest &lt;code&gt;master&lt;/code&gt; in the original repo, not the out-of-date one in my fork.&lt;/p&gt;

&lt;p&gt;The steps are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git remote add upstream git+ssh://git@github.com/emberjs/ember-test-helpers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git fetch upstream master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git merge upstream/master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout -b my-branch-2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push -u origin my-branch-2&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Steps 1-4 are just syncing my local mirror of my fork's &lt;code&gt;master&lt;/code&gt; branch with the &lt;code&gt;master&lt;/code&gt; branch in Ember test helpers' repo. Since I don't actually do anything with my fork's &lt;code&gt;master&lt;/code&gt; branch, I could simplify this slightly:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git remote add upstream git+ssh://git@github.com/emberjs/ember-test-helpers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git fetch upstream master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout -b my-branch-2 upstream/master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push -u origin my-branch-2&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Simplified git forking workflow
&lt;/h2&gt;

&lt;p&gt;The simplification involves a tweak to the workflow that is pretty minor from a mechanical/what-commands-do-I-type standpoint, but I think simplifies the mental model significantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  First pull request
&lt;/h3&gt;

&lt;p&gt;Instead of cloning my fork of the repo, I will clone the original repo and then add my fork as another remote:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork the repo, so the original is at &lt;code&gt;git+ssh://git@github.com/emberjs/ember-test-helpers&lt;/code&gt; and my fork is at &lt;code&gt;git+ssh://git@github.com/bendemboski/ember-test-helpers&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git clone git+ssh://git@github.com/emberjs/ember-test-helpers&lt;/code&gt; &amp;lt;-- this is the key difference&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout -b my-branch-1&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git remote add bendemboski git+ssh://git@github.com/bendemboski/ember-test-helpers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push -u bendemboski my-branch-1&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The server-side result is the same -- my fork has a &lt;code&gt;my-branch-1&lt;/code&gt; branch ready to use for a pull request into the original repo, but the local setup is different in a way that makes opening subsequent pull requests somewhat simpler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Second pull request
&lt;/h3&gt;

&lt;p&gt;Since I have cloned the original repo, I sync my local &lt;code&gt;master&lt;/code&gt; branch just like I would with any other branch, simplifying the beginning of this workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;git checkout master&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git pull&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git checkout -b my-branch-2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Write code&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git push -u bendemboski my-branch-2&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Practical differences
&lt;/h2&gt;

&lt;p&gt;The only practical difference between these two versions of the forking workflow is that in the simplified form I'm not trying to keep my fork's &lt;code&gt;master&lt;/code&gt; up-to-date with the original repo's. In fact, I completely ignore my fork's &lt;code&gt;master&lt;/code&gt; branch and just treat my fork as a repository for pushing temporary branches to support opening pull requests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ergonomic benefits
&lt;/h2&gt;

&lt;p&gt;Even though the pure number of commands I need to type isn't significantly reduced, in my experience, this simplifies the mental model in a way that reduces friction in the whole process of opening pull requests. The benefits I've experienced are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I don't have to worry about whether my fork's &lt;code&gt;master&lt;/code&gt; branch is up-to-date with the original repo's &lt;code&gt;master&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;I don't have to worry about accidentally merging code into my fork's &lt;code&gt;master&lt;/code&gt; branch in a way that would require something like rebase to get back in sync with the original repo's &lt;code&gt;master&lt;/code&gt; branch&lt;/li&gt;
&lt;li&gt;I don't have to think about the fact that I'm working with a fork of a repository aside from the one time (per branch) that I have to push my branch to the remote pointing to my fork (&lt;code&gt;git push -u bendemboski ...&lt;/code&gt; instead of &lt;code&gt;git push -u origin ...&lt;/code&gt;). All of my pulling and branching operations are done just as if I owned the repository, and it's only the first time I push a branch that I have to do something different.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These may not seem like a huge deal, but for me they are, because of the mental simplification of not having to switch between two different "modes" -- the "working on an original repo" mode and the "working on a fork of a repo" mode. When I'm working in my local clone, it makes no difference and I do the same thing either way, and it's only when I need to push a new branch to somewhere remote that I have to think about the difference, and that's exactly when I &lt;em&gt;should&lt;/em&gt; be thinking about the difference!&lt;/p&gt;

&lt;p&gt;This peels off one extra layer of mental load and reduces the friction involved in the whole process.&lt;/p&gt;

&lt;h2&gt;
  
  
  An extra thought
&lt;/h2&gt;

&lt;p&gt;There can be good reasons to use the forking workflow even for repositories that you &lt;em&gt;can&lt;/em&gt; push to, e.g. to keep from polluting the original repository with experimental branches, etc. As the ever-insightful &lt;a href="https://twitter.com/katiegengler"&gt;@katiegengler&lt;/a&gt; pointed out in a Discord discussion, in such cases following the simplified forking workflow but cloning the original repo using the &lt;code&gt;https&lt;/code&gt; instead of &lt;code&gt;git&lt;/code&gt; URL (&lt;code&gt;git clone https://github.com/emberjs/ember-test-helpers&lt;/code&gt; instead of &lt;code&gt;git clone git+ssh://git@github.com/emberjs/ember-test-helpers&lt;/code&gt;) adds an extra layer of protection preventing you from accidentally pushing to the original repo instead of your fork.&lt;/p&gt;

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

&lt;p&gt;I've found this tweak to the workflow to be a non-trivial simplification that noticeably improves my developer experience of periodically contributing to projects that I don't own. I love the open source model, and I love contributing back to projects that I have benefited from, so I'm always excited to find ways of reducing friction in the process to make me more likely to do it, and free up energy for the actual development work that's fun rather than the git mechanics that are...less fun. I'd love to hear what you think about this simplified workflow, or other ways you've found to reduce friction in contributing to open source projects.&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>bitbucket</category>
    </item>
  </channel>
</rss>
