<?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: Jen Chan</title>
    <description>The latest articles on DEV Community by Jen Chan (@jenc).</description>
    <link>https://dev.to/jenc</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%2F20372%2Fed620018-f748-4d1a-a8a3-59e45a64939c.gif</url>
      <title>DEV Community: Jen Chan</title>
      <link>https://dev.to/jenc</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jenc"/>
    <language>en</language>
    <item>
      <title>Who Moved My Cookies? Of Cookies On Subdomains</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Wed, 09 Jul 2025 14:32:53 +0000</pubDate>
      <link>https://dev.to/jenc/who-moved-my-cookies-of-cookies-on-subdomains-4mk7</link>
      <guid>https://dev.to/jenc/who-moved-my-cookies-of-cookies-on-subdomains-4mk7</guid>
      <description>&lt;p&gt;Yes, it's 2025, we had the internet and the encyclopedic knowledge of LLMs at our fingertips, and we were still blocked for 45 minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2Fstatic%2Fimages%2Fblog%2Fwho-moved-my-cookies%2Fmaze-game-kids-cute-groovy-cookie-with-pieces-chocolate-looking-way-gift-box_563230-596.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2Fstatic%2Fimages%2Fblog%2Fwho-moved-my-cookies%2Fmaze-game-kids-cute-groovy-cookie-with-pieces-chocolate-looking-way-gift-box_563230-596.jpg" alt="Maze game for kids cute groovy cookie with pieces of chocolate looking for a way to the gift box " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.freepik.com/premium-vector/maze-game-kids-cute-groovy-cookie-with-pieces-chocolate-looking-way-gift-box_55883162.htm" rel="noopener noreferrer"&gt;"Maze game for kids cute groovy cookie with pieces of chocolate looking for a way to the gift box"&lt;/a&gt; by &lt;a href="https://www.freepik.com/author/mariabullfinch" rel="noopener noreferrer"&gt;Maria Kololeva&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Imagine, it's possible for a gaming platform to have all of these namespaces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;addictinggames.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;fun.addictinggames.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cool.addictinggames.com&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And other mirror domains could also receive its traffic in the event some of them got blocked by parental controls or libraries, or some no-fun workplaces want to prevent people from playing fun games:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;addictinggames.wtf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;addictinggames.lol&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;evenmoreaddictinggames.com&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;playaddicting.fun&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Such a system was set up so that if Johnny was logged in at &lt;code&gt;addictinggames.com&lt;/code&gt; but his mom decided to block access to the site, he'd would be able to use &lt;code&gt;addictinggamesmirror.com&lt;/code&gt; to redirect his session to &lt;code&gt;addictinggames.wtf&lt;/code&gt; and continue playing there. ✌&lt;/p&gt;

&lt;p&gt;So, we were going to launch a web 3 game at a subdomain, &lt;code&gt;even.more.addictinggames.com&lt;/code&gt;... with the intention that sessions could be persisted to &lt;code&gt;even.more.addictinggames.wtf&lt;/code&gt;, &lt;code&gt;even.more.additinggames.lol&lt;/code&gt;, so on and so forth.&lt;/p&gt;

&lt;p&gt;How hard could it be?&lt;/p&gt;

&lt;p&gt;It was impossible.&lt;/p&gt;

&lt;p&gt;We had previously used subdomains and mirror domains to persist user sessions for authenticating users.&lt;/p&gt;

&lt;p&gt;The cookie was just proof that the user was still authenticated -amongst other things- after a redirect.&lt;/p&gt;

&lt;p&gt;While all other areas of the platform showed that a user who had logged in and went back to the view was still authenticated, the proxied request returned nothing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we tried
&lt;/h2&gt;

&lt;p&gt;We of course did the usual cookie clearing, logging out and logging in and checking the dev tools.&lt;/p&gt;

&lt;p&gt;We checked that there wasn't anything on the client side that was setting or eliminating the cookie –an anti-pattern anyway.&lt;/p&gt;

&lt;p&gt;Since the cookie was HTTP-Only, there wouldn't have been any way for the client to access it.&lt;/p&gt;

&lt;p&gt;We checked that the cookie was being set on the server side.&lt;/p&gt;

&lt;p&gt;We compared headers from the proxied request and the original request.&lt;/p&gt;

&lt;p&gt;We tried desperately to specify the destination domain for &lt;code&gt;Set-Cookie&lt;/code&gt; to be &lt;code&gt;*.addictinggames.com&lt;/code&gt; or &lt;code&gt;even.more.addictinggames.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Our manager pointed us to the MDN docs on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies" rel="noopener noreferrer"&gt;cookies&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As it turns out:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the Set-Cookie header does not specify a Domain attribute, the cookies are available on the server that sets it but not on its subdomains. Therefore, specifying Domain is less restrictive than omitting it. Note that a server can only set the Domain attribute to its own domain or a parent domain, not to a subdomain or some other domain.&lt;br&gt;
So, for example, a server with domain &lt;code&gt;foo.example.com&lt;/code&gt; could set the attribute to &lt;code&gt;example.com&lt;/code&gt; or &lt;code&gt;foo.example.com&lt;/code&gt;, but not &lt;code&gt;bar.foo.example.com&lt;/code&gt; or &lt;code&gt;elsewhere.com&lt;/code&gt; (the cookies would still be sent to subdomains such as &lt;code&gt;bar.foo.example.com&lt;/code&gt; though).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See &lt;a href="https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3" rel="noopener noreferrer"&gt;RFC 6265 Section 4.1.2.3&lt;/a&gt; on "HTTP State Management Mechanism".&lt;/p&gt;

&lt;h2&gt;
  
  
  One does not simply set a cookie on a subdomain
&lt;/h2&gt;

&lt;p&gt;✅ We could expect that a server that responds with the &lt;code&gt;Set-Cookie&lt;/code&gt; header and no &lt;code&gt;Domain&lt;/code&gt; specified, would only set the cookie to the host server of the document, but not at subdomains.&lt;/p&gt;

&lt;p&gt;✅ We could expect that a server responding with the &lt;code&gt;Set-Cookie&lt;/code&gt; header and &lt;code&gt;Domain&lt;/code&gt; specified to &lt;code&gt;addictinggames.com&lt;/code&gt;, would allow &lt;code&gt;addictinggames.com&lt;/code&gt; and subdomains like &lt;code&gt;fun.addictinggames.com&lt;/code&gt; to receive the cookie.&lt;/p&gt;

&lt;p&gt;✅ We can also use the &lt;code&gt;Set-Cookie&lt;/code&gt; header to set a &lt;code&gt;Domain&lt;/code&gt; attribute to &lt;code&gt;fun.addictinggames.com&lt;/code&gt; and expect the cookie to be set when visiting &lt;code&gt;fun.addictinggames.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;❌ We can't use the &lt;code&gt;Set-Cookie&lt;/code&gt; header to set a &lt;code&gt;Domain&lt;/code&gt; attribute to &lt;code&gt;even.more.addictinggames.com&lt;/code&gt; or &lt;code&gt;even.more.addictinggames.wtf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So it follows that it would be another magnitude of problematic to expect such a cookie to persist on an external mirror subdomain like &lt;code&gt;even.more.addictinggames.wtf&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;p&gt;Sometimes web APIs defy logic.&lt;/p&gt;

&lt;p&gt;Don't try to set cookies on a subdomain-of-a-subdomain. Just stick with one level or the parent domain.&lt;/p&gt;

&lt;p&gt;Instead of dark-launching on &lt;code&gt;even.more.addictinggames.com&lt;/code&gt;, we went with &lt;code&gt;more.addictinggames.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Ta-da!&lt;/p&gt;

</description>
      <category>cookies</category>
      <category>webdev</category>
      <category>http</category>
    </item>
    <item>
      <title>Block AI Crawlers, Save Tears: Ditching Vercel for Cloudflare Pages</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Fri, 04 Jul 2025 21:21:53 +0000</pubDate>
      <link>https://dev.to/jenc/block-ai-crawlers-save-tears-ditching-vercel-for-cloudflare-pages-2846</link>
      <guid>https://dev.to/jenc/block-ai-crawlers-save-tears-ditching-vercel-for-cloudflare-pages-2846</guid>
      <description>&lt;p&gt;Have you tried self-hosting with Next.js? What was your experience like?&lt;/p&gt;

&lt;p&gt;Cloudflare recently announced &lt;a href="https://blog.cloudflare.com/block-ai-bots/" rel="noopener noreferrer"&gt;their decision&lt;/a&gt; to block AI crawlers for customers who host with them and offer &lt;a href="https://blog.cloudflare.com/introducing-pay-per-crawl/" rel="noopener noreferrer"&gt;pay-per-crawl in private beta&lt;/a&gt;, which is quite exciting given the declining usability and relevance of Google search.&lt;/p&gt;

&lt;p&gt;As a small gesture of meaningless resistance under political and economic despair, I'm migrating my Next.js blog from Vercel to Cloudflare Pages.&lt;/p&gt;

&lt;p&gt;I want my blog to be discovered and read by &lt;strong&gt;humans&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;In principle, listing bots you want to disallow from crawling your site in a &lt;a href="https://www.robotstxt.org/" rel="noopener noreferrer"&gt;robots.txt&lt;/a&gt; is a first line of defense, but &lt;a href="https://www.reuters.com/technology/artificial-intelligence/multiple-ai-companies-bypassing-web-standard-scrape-publisher-sites-licensing-2024-06-21/" rel="noopener noreferrer"&gt;many crawlers don't adhere to it&lt;/a&gt;.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; Last year, Perplexity was found to be using crawlers that spoof the user agent, such that their bots appear to be human browsing the site.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Even on a free plan with workers or pages, Cloudflare has multiple features that make it easy to block and monitor AI crawlers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the option to &lt;a href="https://developers.cloudflare.com/bots/additional-configurations/managed-robots-txt/" rel="noopener noreferrer"&gt;auto-generate&lt;/a&gt; a &lt;code&gt;robots.txt&lt;/code&gt; file that covers the popular AI bots&lt;/li&gt;
&lt;li&gt;a dashboard that allows you to monitor AI crawler requests, and block them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fblocking-ai-crawlers%252Fcloudflare-ai-audit-bot.png%26w%3D3840%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fblocking-ai-crawlers%252Fcloudflare-ai-audit-bot.png%26w%3D3840%26q%3D75" alt="screenshot of Cloudflare AI Audit Crawlers Dashboard " width="2351" height="1569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an &lt;a href="https://developers.cloudflare.com/ai-audit/" rel="noopener noreferrer"&gt;AI audit&lt;/a&gt; feature (currently in beta) that allows you to manage and monitor AI crawlers, and register verified bots.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fblocking-ai-crawlers%252Fcloudflare-ai-audit.png%26w%3D3840%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fblocking-ai-crawlers%252Fcloudflare-ai-audit.png%26w%3D3840%26q%3D75" alt="screenshot of Cloudflare AI Audit Metrics " width="2386" height="2897"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's been 2 days. Interesting to see what hits.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next.js is fun and sexy when you work with boring technologies
&lt;/h2&gt;

&lt;p&gt;Next.js is intoxicating. I was hooked on it up to version 12. At the time, no other meta-framework provided the control it did for being able to alternate between static site generation, server-side rendering and client-side rendering all within the same application. It was (and still is), quite easy to keep up with. (...Until they started confusing the newcomers and experienced folks alike with the &lt;a href="https://nextjs.org/docs/pages/getting-started/project-structure" rel="noopener noreferrer"&gt;pages router&lt;/a&gt; and &lt;a href="https://nextjs.org/docs/app" rel="noopener noreferrer"&gt;app router&lt;/a&gt; ... &lt;em&gt;shakes fist&lt;/em&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fblocking-ai-crawlers%252Fspongebob-next.png%26w%3D1200%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fblocking-ai-crawlers%252Fspongebob-next.png%26w%3D1200%26q%3D75" alt="Spongebob laughing meme with capitalized bold font on top of collage of laughing spongebobs. The text reads 'You work with Next.js? What a luxury!'" width="547" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like Heroku, Vercel -previously &lt;a href="https://vercel.com/blog/zeit-is-now-vercel" rel="noopener noreferrer"&gt;&lt;code&gt;now&lt;/code&gt;&lt;/a&gt;- was a gateway platform for getting into CI/CD. Both Next and Vercel built a wonderful reputation in the community for Scandinavian web design, comprehensive documentation and basically, the ability to go to market without worrying about infrastructure.&lt;/p&gt;

&lt;p&gt;It also happens get more expensive as you scale, especially if you want your site highly available in different regions and have high traffic.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Check out these Reddit threads 🍿&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;r/nextjs&lt;/code&gt; &lt;a href="https://www.reddit.com/r/nextjs/comments/1ak8kab/vercel_pricing_20m_requestsmonth/" rel="noopener noreferrer"&gt;"Vercel pricing 20m requests/month"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r/devops&lt;/code&gt; &lt;a href="https://www.reddit.com/r/devops/comments/1450000/why_use_vercel_when_you_can_go_direct_aws/?rdt=36229" rel="noopener noreferrer"&gt;"Why use Vercel when you can go direct AWS ? especially for small projects"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r/nextjs&lt;/code&gt; &lt;a href="https://www.reddit.com/r/nextjs/comments/1aiicc4/vercel_pro_pricing_is_fubar/" rel="noopener noreferrer"&gt;"Vercel pro pricing is fubar"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r/nextjs&lt;/code&gt; &lt;a href="https://www.reddit.com/r/nextjs/comments/1fo5etb/when_does_vercel_become_expensive/" rel="noopener noreferrer"&gt;"When does Vercel become expensive?"&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I heard about the proverbial hell with deploying Next.js outside Vercel. As a result, &lt;a href="https://opennext.js.org/" rel="noopener noreferrer"&gt;OpenNext&lt;/a&gt; was started as a vendor-backed community project to adapt Next.js to Cloudflare, AWS and Netlify. I had to try it for myself.&lt;/p&gt;

&lt;p&gt;I wanted to see how easy it would be to deploy my &lt;em&gt;mostly&lt;/em&gt; static Next.js blog off Vercel, and I wasn't entirely disappointed – if you're patient with getting past the &lt;a href="https://jenchan.biz/glossary#:~:text=Phantom%20failures%20of%20CI" rel="noopener noreferrer"&gt;phantom build errors&lt;/a&gt;, &lt;a href="https://github.com/vercel/next.js/issues/53220" rel="noopener noreferrer"&gt;prerendering issues&lt;/a&gt; and remembering which &lt;a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/#supported-nodejs-apis" rel="noopener noreferrer"&gt;Node.js APIs are supported by Cloudflare&lt;/a&gt;.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas
&lt;/h2&gt;

&lt;p&gt;I followed this guide for &lt;a href="https://developers.cloudflare.com/pages/framework-guides/nextjs/ssr/get-started/#existing-apps" rel="noopener noreferrer"&gt;deploying an existing Next.js site to Cloudflare Pages&lt;/a&gt;, but also found certain oddities that made this take 6 hours to get to a manual deploy from CLI, and another 6 to automate on Github Actions. And yes, I was troubleshooting with AI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bring your own domain
&lt;/h3&gt;

&lt;p&gt;For a while, I had set up my DNS to proxy requests to Vercel's IP addresses. (See Github Issue &lt;a href="https://github.com/vercel/vercel/discussions/7739" rel="noopener noreferrer"&gt;#7739&lt;/a&gt;) But if you intend to use Cloudflare for edge computing, analytics or monitoring, then you might get the most benefit out of transferring your DNS to Cloudflare.&lt;/p&gt;

&lt;p&gt;I got mine for $15 CAD/year. Goodbye Dreamhost 😤!&lt;/p&gt;

&lt;h3&gt;
  
  
  Update your Content Security Policy
&lt;/h3&gt;

&lt;p&gt;You'll need to update your Content Security Policy to allow Cloudflare to run analytics and track real user data.&lt;/p&gt;

&lt;h3&gt;
  
  
  Prevent linters from corrupting your &lt;code&gt;wrangler.jsonc&lt;/code&gt; with a dangling comma
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;cough&lt;/em&gt;, prettier, &lt;em&gt;cough&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Something in my boilerplate repo was causing the linter to add a dangling comma before the closing bracket of my &lt;code&gt;wrangler.jsonc&lt;/code&gt; config file.&lt;/p&gt;

&lt;p&gt;Your build will fail when it reaches Cloudflare if you have a dangling comma.&lt;/p&gt;

&lt;h3&gt;
  
  
  Route-based rendering adjustments
&lt;/h3&gt;

&lt;p&gt;If you have any server functions or SSR-ed pages, routes that use these functions must target the edge runtime with:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Build Time Noise
&lt;/h3&gt;

&lt;p&gt;Every half year or so I have to maintain this Next.js blog and I'll find some new build error after pulling upstream from the template repo. I don't know if it's because I'm using a template repo with a lot of dependencies, or if modern front end frameworks are less stable than a Jekyll site might be.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://nextjs.org/docs/messages/prerender-error" rel="noopener noreferrer"&gt;Prerendering errors&lt;/a&gt; may happen on views containing server components or dynamic routes that are statically generated but rely on server functions to populate data that would appear on the page.&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="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;24.622&lt;/span&gt;    &lt;span class="err"&gt;⚡️&lt;/span&gt; &lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="sr"&gt;/blog/&lt;/span&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;24.622&lt;/span&gt;    &lt;span class="err"&gt;⚡️&lt;/span&gt; &lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="sr"&gt;/blog/&lt;/span&gt;&lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;rsc&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;24.690&lt;/span&gt;    &lt;span class="err"&gt;⚡️&lt;/span&gt; &lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="sr"&gt;/tags/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;24.691&lt;/span&gt;    &lt;span class="err"&gt;⚡️&lt;/span&gt; &lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="sr"&gt;/tags/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;rsc&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;24.691&lt;/span&gt;    &lt;span class="err"&gt;⚡️&lt;/span&gt; &lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="sr"&gt;/tags/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;34&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;24.691&lt;/span&gt;    &lt;span class="err"&gt;⚡️&lt;/span&gt; &lt;span class="nx"&gt;Invalid&lt;/span&gt; &lt;span class="nx"&gt;prerender&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="sr"&gt;/tags/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;rsc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was ultimately benign but annoying.&lt;/p&gt;

&lt;h3&gt;
  
  
  ESM conversion
&lt;/h3&gt;

&lt;p&gt;I had to update my eslint, tailwind, and prettier config and &lt;code&gt;package.json&lt;/code&gt; type property to support ESM &lt;code&gt;imports&lt;/code&gt; and default exports. Then I updated &lt;code&gt;next.config.js&lt;/code&gt; to &lt;code&gt;next.config.mjs&lt;/code&gt; and use ESM named imports instead of CommonJS' &lt;code&gt;require&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If your Next plugins haven't been imported as ESModules, you'll have to convert them to ESM. Unlike the commonJS version of next.config.js, there is no &lt;code&gt;module.exports.plugins&lt;/code&gt; property, and you have to use the plugin as if it's a higher-order function taking the config object as an argument.&lt;/p&gt;

&lt;p&gt;Before&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;withContentlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-contentlayer2&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="nf"&gt;withContentlayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;withContentlayer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;withBundleAnalyzer&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;After&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;createContentlayerPlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;next-contentlayer2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Use createContentlayerPlugin instead of withContentlayer&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;withContentlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContentlayerPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;configPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./contentlayer.config.ts&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;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;
&lt;span class="nf"&gt;withContentlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add Cloudflare's Github App &lt;strong&gt;Integration&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The Github app, ["Cloudflare Workers and Pages"]( can be installed (&lt;a href="https://github.com/apps/cloudflare-workers-and-pages" rel="noopener noreferrer"&gt;https://github.com/apps/cloudflare-workers-and-pages&lt;/a&gt;) to enable automatic builds after pushing to main.&lt;/p&gt;

&lt;p&gt;I had initially set up Github Actions Workflow with the &lt;code&gt;CLOUDFLARE_API_TOKEN&lt;/code&gt; environment variable to build the project, then deploy the static site on Cloudflare. I found that the automatic builds on Cloudflare's side was much faster, while server side calls were &lt;a href="https://en.wikipedia.org/wiki/HTTP_429" rel="noopener noreferrer"&gt;429-ing&lt;/a&gt; on my &lt;a href="https://github.com/usrrname/jenchan.biz/actions/runs/16077774615/job/45376766250" rel="noopener noreferrer"&gt;Github Actions workflow&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Self-hosting Next.js full stack apps will require more legwork
&lt;/h3&gt;

&lt;p&gt;If you're deploying a full stack app instead of a completely static site, &lt;strong&gt;DON'T&lt;/strong&gt; use &lt;code&gt;@cloudflare/next-on-pages&lt;/code&gt;. Use &lt;a href="https://www.npmjs.com/package/@opennextjs/cloudflare" rel="noopener noreferrer"&gt;@opennextjs/cloudflare&lt;/a&gt; instead.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Workers runtime supports a broad set of Node.js APIs — but the Next.js Edge Runtime code intentionally constrains this &lt;a href="https://nextjs.org/docs/app/api-reference/edge#reference" rel="noopener noreferrer"&gt;↗&lt;/a&gt;. As a result, only the following Node.js APIs will work in your Next.js app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;buffer&lt;/li&gt;
&lt;li&gt;events&lt;/li&gt;
&lt;li&gt;assert&lt;/li&gt;
&lt;li&gt;util&lt;/li&gt;
&lt;li&gt;async_hooks&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe I'll try this Next (ha ha).&lt;/p&gt;

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

&lt;p&gt;Moving from Vercel to Cloudflare Pages was a petty decision driven by the need to scratch a CI itch in the dead heat of nuclear AI summer.&lt;/p&gt;

&lt;p&gt;It's not impossible to deploy Next.js off Vercel, but it does take longer than usual due to Cloudflare's and Vercel's limited support for Node.js APIs.&lt;/p&gt;

&lt;p&gt;As the ecosystem of tools and platforms become increasingly subscription-driven and stack-based, developers and businesses alike will need to make deliberate choices to avoid vendor lock-in and bot-proof their intellectual property.&lt;/p&gt;

&lt;p&gt;I could see challenges with applying this to a larger fullstack enterprise project where multiple services and edge-related features would need to be migrated or rewritten and re-configured on Cloudflare, but where you have an able developer, the cost savings could be substantial.&lt;/p&gt;

&lt;h3&gt;
  
  
  Virtue Signal 🎀
&lt;/h3&gt;

&lt;p&gt;I wasn't paid by Vercel or Cloudflare to write this. I am however, looking for a new role where I can dabble with this kind of stuff (◕‿◕✿)&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Reading
&lt;/h2&gt;

&lt;p&gt;Brandon Bayer. &lt;a href="https://www.flightcontrol.dev/blog/secret-knowledge-to-self-host-nextjs" rel="noopener noreferrer"&gt;"Secret Knowledge to Self-Host Next.js"&lt;/a&gt;, &lt;a href="https://www.flightcontrol.dev/" rel="noopener noreferrer"&gt;FlightControl&lt;/a&gt;, 2024.&lt;/p&gt;

&lt;p&gt;Indie Starter.&lt;a href="https://indie-starter.dev/blog/the-cost-structure-of-using-serverless-functions-on-vercel" rel="noopener noreferrer"&gt;"The Cost Structure of Using Serverless Functions on Vercel"&lt;/a&gt;, Oct 8,2024.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;"Several AI companies are bypassing web standards to scrape publisher sites, licensing content and paying for access to news sites, according to a Reuters investigation." - &lt;a href="https://www.reuters.com/technology/artificial-intelligence/multiple-ai-companies-bypassing-web-standard-scrape-publisher-sites-licensing-2024-06-21/" rel="noopener noreferrer"&gt;Reuters&lt;/a&gt;, Jun 21, 2024. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Dhruv Bansal. &lt;a href="https://www.wired.com/story/perplexity-is-a-bullshit-machine/" rel="noopener noreferrer"&gt;"Perplexity is a Bullshit Machine"&lt;/a&gt;, Wired, Jun 19, 2024. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Sushrit P K. &lt;a href="https://medium.com/@sushrit.pk21/how-when-and-why-you-should-switch-from-vercel-to-a-different-hosting-provider-especially-for-8ba25e439788" rel="noopener noreferrer"&gt;"How, When, and Why you should switch from Vercel to a different Hosting Provider (Especially for Next.js)"&lt;/a&gt;, Feb 2022. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Cloudflare. "Node.js compatibility: &lt;a href="https://developers.cloudflare.com/workers/runtime-apis/nodejs/#supported-nodejs-apis" rel="noopener noreferrer"&gt;Supported Node.js APIs&lt;/a&gt;", Apr 7, 2025. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>showdev</category>
      <category>nextjs</category>
      <category>devops</category>
      <category>cloudflare</category>
    </item>
    <item>
      <title>The Thankless Complexity of Custom Form Validations</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Thu, 03 Apr 2025 01:44:24 +0000</pubDate>
      <link>https://dev.to/jenc/the-thankless-complexity-of-custom-form-validations-5hi3</link>
      <guid>https://dev.to/jenc/the-thankless-complexity-of-custom-form-validations-5hi3</guid>
      <description>&lt;p&gt;One of the least exhilarating but common development tasks are building forms, and form validations. While important, they're often over-designed and easy to over-engineer as a result.&lt;/p&gt;

&lt;p&gt;I believe we've gone too far with trying to accomodate &lt;em&gt;all&lt;/em&gt; kinds of form validation while building reusable input fields –– especially for component libraries and design systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Browser Already Handles It
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi0.wp.com%2Fcss-tricks.com%2Fwp-content%2Fuploads%2F2017%2F06%2Frequired-input.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi0.wp.com%2Fcss-tricks.com%2Fwp-content%2Fuploads%2F2017%2F06%2Frequired-input.png" alt="screenshot of a validation error on chrome"&gt;&lt;/a&gt;&lt;br&gt; Default HTML5 on-submit validation in Chrome. This is what happens when the submit event is called on a form and the incorrect data or required fields are not filled as a result of the HTML &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation" rel="noopener noreferrer"&gt;constraint validation API&lt;/a&gt; calling &lt;code&gt;checkValidity()&lt;/code&gt; on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/ElementInternals" rel="noopener noreferrer"&gt;ElementInternals&lt;/a&gt; or &lt;code&gt;form.validate()&lt;/code&gt; methods.
 &lt;/p&gt;

&lt;p&gt;The most basic validation happens on submit, when the user hits a "submit" button associated with a form element.&lt;/p&gt;

&lt;p&gt;It's not styled nicely. It doesn't look or feel consistent across browsers, but &lt;strong&gt;IT JUST WORKS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Form input elements have &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation#using_built-in_form_validation" rel="noopener noreferrer"&gt;built-in validation&lt;/a&gt; &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Constraint_validation#validation-related_attributes" rel="noopener noreferrer"&gt;attributes&lt;/a&gt; for &lt;code&gt;min&lt;/code&gt;, &lt;code&gt;max&lt;/code&gt;,&lt;code&gt;step&lt;/code&gt;, &lt;code&gt;minlength&lt;/code&gt;, &lt;code&gt;maxlength&lt;/code&gt; values, and regex matching on the &lt;code&gt;pattern&lt;/code&gt; attribute for things like phone number format masking.&lt;/p&gt;

&lt;p&gt;If you stick to validating on submit, could very well validate a form without using Javascript! &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The drawback: errors that show up from failed submission attempts won't disappear til the user retries submitting the form.&lt;/p&gt;

&lt;h2&gt;
  
  
  All the ways to validate
&lt;/h2&gt;

&lt;p&gt;Each type requires invoking validation on the form field at different moments of the user journey.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Validation as the user interacts with a field (&lt;code&gt;oninput&lt;/code&gt;, &lt;code&gt;onchange&lt;/code&gt;, &lt;code&gt;onkeypress&lt;/code&gt;). Also called "&lt;strong&gt;live validation&lt;/strong&gt;".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1n0tp1849le10whwk42.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr1n0tp1849le10whwk42.gif" alt="Gif of Slack's implementation of live password validation. The gif shows two form fields: one is for entering a password, the other is for confirming the password. The first field shows a meter below a password field progressing from red to green as the length and content of the password fulfills requirements"&gt;&lt;/a&gt;&lt;/p&gt;
Slack's implementation of live validation on a password field with a strength meter under the field provides instant feedback on whether the user's password fulfills password rules.





&lt;p&gt;Validation that runs when the user pauses, or has finished typing in a field (&lt;code&gt;onkeyup&lt;/code&gt; with debounce) is less common besides finding them on username and password fields. I don't think it's necessary to including such behaviour on regular input fields in component libraries.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Validation that runs &lt;strong&gt;on submission&lt;/strong&gt; (&lt;code&gt;onsubmit&lt;/code&gt;) - and should prevent any erroneous data being posted if there are errors in any form field.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Validation that shows on fields &lt;strong&gt;after form submission&lt;/strong&gt; due to responses from the server&lt;/p&gt;

&lt;p&gt;One would expect that post-submission, server-side errors are reflected on the form, under corresponding fields that had incorrect content.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Validation that runs &lt;strong&gt;right after&lt;/strong&gt; the user has interacted with the field (&lt;code&gt;onblur&lt;/code&gt;) - otherwise called "late validation"&lt;/p&gt;

&lt;p&gt;Unpopular opinion: I recommend sticking to this when there is a need to build ultra-fast. You will dig your own grave otherwise.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agreeing to implement live validation, if not scoped carefully, will result in hellfire from all directions since:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;validation can run &lt;a href="https://baymard.com/blog/inline-form-validation#pitfall-1-premature-inline-validation" rel="noopener noreferrer"&gt;prematurely and aggressively&lt;/a&gt;, especially on fields that are required but incomplete.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you'll be playing whackamole to remove validation errors whenever they're corrected, which means detecting change &lt;code&gt;oninput&lt;/code&gt; or &lt;code&gt;onkeypress&lt;/code&gt; and removing the error message when the field is valid again, particularly on fields that are dependent on each other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;you end up tempted to add more feedback mechanisms like toasts to prove the form submitted&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Treacherous Variants
&lt;/h2&gt;

&lt;p&gt;Let's look at some well-intentioned UX patterns that explode the combinatories of visual and accessiblity testing since form layouts are so varied.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fformat%3Awebp%2F1%2AQ2ZvXIuTtJePjjAZWdSbmg.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fv2%2Fformat%3Awebp%2F1%2AQ2ZvXIuTtJePjjAZWdSbmg.jpeg" alt="Screenshot of designs for different forms of input field validation by Andrew Coyle"&gt;&lt;/a&gt;&lt;/p&gt;

Andrew Coyle, &lt;a href="https://coyleandrew.medium.com/forms-need-validation-2ecbccbacea1" rel="noopener noreferrer"&gt;"Forms need validation"&lt;/a&gt;, Medium, Dec 16. 2016



&lt;ol&gt;
&lt;li&gt;The option to show validation messages above, below, or adjacent to a form field&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Showing validation errors inside a tooltip or popover&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Showing &lt;strong&gt;multiple&lt;/strong&gt; validation messages above, below, adjacent to any form field 😱&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The heavily debated best practice of indicating required inputs with an asterisk, OR appending "(required)" to the field label, OR only showing "(optional)" next to optional field labels.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Showing a summary of form errors at the top or bottom of the form on-submission&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2t4lerp5qa4w8a6865b2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2t4lerp5qa4w8a6865b2.png" alt="Screenshot of UX convention for marking form fields as required or optional"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;W3C, &lt;a href="https://www.w3.org/WAI/eval/preliminary.html" rel="noopener noreferrer"&gt;"Easy Checks - A First Review of Web Accessibility"&lt;/a&gt;, 2023&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hd4smag3s5gk6t490mm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7hd4smag3s5gk6t490mm.png" alt="A mobile design illustrating a summary of errors at the top of the form after submission"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://design.mindsphere.io/patterns/form-validation.html" rel="noopener noreferrer"&gt;"Form Validation"&lt;/a&gt;, Siemens UI Design and Patterns, User Experience Toolkit&lt;/p&gt;

&lt;h2&gt;
  
  
  To Make a River Flow Backwards
&lt;/h2&gt;

&lt;p&gt;Developers easily do more absurd work to make validation possible.&lt;/p&gt;

&lt;p&gt;2 examples of how garden-variety form fields grow more complex than they should be:&lt;/p&gt;

&lt;h3&gt;
  
  
  Live validation, all the time.
&lt;/h3&gt;

&lt;p&gt;Until a year ago, developers had to deliberately prevent live validation from running on page load if it was a requirement.&lt;/p&gt;

&lt;p&gt;When the form is first rendered, the fields are empty, and immediate check on validation by browser APIs will show the &lt;code&gt;:invalid&lt;/code&gt; styles on the fields with the &lt;code&gt;required&lt;/code&gt; attribute set.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/usrrname/embed/preview/VYZrMww?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;We'd have to tell the form &lt;strong&gt;not&lt;/strong&gt; to display invalid styles, especially those with the &lt;code&gt;required&lt;/code&gt; attribute set while validating empty fields on initial page load. Alternately, we might opt to prevent live validation from happening until the user interacts with a field after initial page load.&lt;/p&gt;

&lt;p&gt;Luckily, the &lt;code&gt;:user-valid&lt;/code&gt; and &lt;code&gt;:user-invalid&lt;/code&gt; pseudo-class CSS selectors just reached &lt;a href="https://web.dev/articles/user-valid-and-user-invalid-pseudo-classes" rel="noopener noreferrer"&gt;baseline status&lt;/a&gt; in 2023, so we can style the form fields based on user interaction instead of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/:invalid" rel="noopener noreferrer"&gt;&lt;code&gt;:invalid&lt;/code&gt;&lt;/a&gt;, which reflects field element's validity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Showing &lt;strong&gt;multiple&lt;/strong&gt; messages above and below a form field
&lt;/h3&gt;

&lt;p&gt;We'll just conditionally render more icon-message molecules as they arise 😛&lt;/p&gt;

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

&lt;p&gt;Displaying multiple inline validations on columned form layouts with multiple fields in a row causes layout shifts and misalignment when space the element occupies dynamically changes whenever the validation message appears.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dgqlksxwzc2hzm9brx1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2dgqlksxwzc2hzm9brx1.png" alt="Screenshot of a Bootstrap style form with multiple columns and input errors displaying above form fields"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Avoid all-in-one solutions
&lt;/h2&gt;

&lt;p&gt;Can you imagine a text input component that on top of its usual props, includes a littany of optional props or attributes like &lt;code&gt;validationMessagePosition&lt;/code&gt;, &lt;code&gt;isContentVisible&lt;/code&gt;, &lt;code&gt;showValidationInTooltip&lt;/code&gt;, &lt;code&gt;validationTooltipPosition&lt;/code&gt;? Yuck. (I recall this &lt;a href="https://www.youtube.com/embed/fHQ1WSx41CA?si=fV1r5lENFsYZv6sQ&amp;amp;start=1299" rel="noopener noreferrer"&gt;AirBnB talk&lt;/a&gt; about how a button component festered in attempt to support all variants.)&lt;/p&gt;

&lt;p&gt;If you're working on a design system, developer adoption of form elements will require clear working examples of how inputs will interact with form elements to show validation - especially if you're supporting multiple frameworks.&lt;/p&gt;

&lt;p&gt;Before you build, clarify base cases that you're supporting with and spend time solutioning for how users can extend form elements.&lt;/p&gt;

&lt;p&gt;It may look smart to build the all-knowing form element that will alternate between displaying the correct visual features for numeric, text, currency types in a single field, and conditionally render the right input... right?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IT'S A TRAP&lt;/strong&gt; – even if many attributes between number and text inputs are similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be Skeptical of New Wheels
&lt;/h2&gt;

&lt;p&gt;Sometimes people think they're being clever by designing or building the world's most usable datepicker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7ospt6x6o07rmirnlv6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy7ospt6x6o07rmirnlv6.jpg" alt="Screenshot of custom date input with separate fields"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any efforts to reinvent this pattern is signing yourself up for 4 times the work and even more errors.&lt;/p&gt;

&lt;p&gt;There's a reason why &lt;code&gt;&amp;lt;input type="date"/&amp;gt;&lt;/code&gt; exists.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;value&lt;/code&gt; it gathers is an ISO 8601 &lt;code&gt;"YYYY-MM-DD"&lt;/code&gt; string but it comes with inherent APIs, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/valueAsNumber" rel="noopener noreferrer"&gt;&lt;code&gt;valueAsNumber&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/valueAsDate" rel="noopener noreferrer"&gt;&lt;code&gt;valueAsDate&lt;/code&gt;&lt;/a&gt; that returns an easily transformable Unix epoch timestamp and Date object respectively to make date and time calculations easier.&lt;/p&gt;

&lt;p&gt;Creating multi-input datepickers from scratch sets you up for exponential efforts with testing and maintenance as you'll have to account for differences in leap years, locale, daylight savings time, and more. You'll acheive more reliable input and validation with an HTML5 native date input.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubrba2sr7t755qdiqo0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fubrba2sr7t755qdiqo0l.png" alt="Screenshot of a man sitting at a table in a park with the banner Change my mind"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom validation: the developer's dilemma
&lt;/h2&gt;

&lt;p&gt;Careful separation between the presentational and functional responsibilities of different input fields, building mechanisms to accept validation rules, and offering users mental models of how to achieve custom validation with abstractions is vital to developer adoption of form elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  The best kind of validation you're going to offer is the kind that you won't  have to write.
&lt;/h2&gt;

&lt;p&gt;Most developers who have to cram-build forms have a stack-based favorite for runtime validation. And most libraries do this with a config object or wrapping the native &lt;code&gt;ConstraintValidation&lt;/code&gt; properties.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Angular's forms has the &lt;code&gt;dirty&lt;/code&gt; and &lt;code&gt;touched&lt;/code&gt; flags to indicate if a field has been interacted with. Inputs can extended by &lt;a href="https://angular.io/api/forms/Validators" rel="noopener noreferrer"&gt;&lt;code&gt;Validators&lt;/code&gt;&lt;/a&gt; to manage validation rules, while &lt;a href="https://angular.io/api/forms/FormControl" rel="noopener noreferrer"&gt;&lt;code&gt;FormControl&lt;/code&gt;&lt;/a&gt; tracks the validation state of the field.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For Vue or Nuxt, &lt;a href="https://formkit.com/essentials/validation" rel="noopener noreferrer"&gt;Formkit&lt;/a&gt; offers &lt;code&gt;validation-config&lt;/code&gt; props for declaring validation type on the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; level. It accepts an object of ids and errors which can be reactively updated to include any errors that are pertinent to the form state. The &lt;code&gt;@formkit/zod&lt;/code&gt; plugin allows Zod to be used for runtime validation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://react-hook-form.com/get-started" rel="noopener noreferrer"&gt;React Hook Form&lt;/a&gt; uses &lt;code&gt;useForm&lt;/code&gt; and &lt;code&gt;useField&lt;/code&gt; hooks to manage form state and validation.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For cases that require showing multiple validations, publishing examples of how users can customize and extend existing components to achieve their goals while integrating with their stack's ecosystem would be a path of lesser resistance than ensuring every field packs in all possible validation scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Web components: The special snowflake
&lt;/h2&gt;

&lt;p&gt;Implementing &lt;a href="https://html.spec.whatwg.org/dev/custom-elements.html" rel="noopener noreferrer"&gt;Form Associated Custom Elements&lt;/a&gt; (aka. form element web components) can be frustrating when there are no dominant patterns established on writing web components for reuse, particularly when there is a need to expose the &lt;code&gt;ValidityState&lt;/code&gt; API to enable the display of multiple, conditional errors.&lt;/p&gt;

&lt;p&gt;Look at how much vanilla code you'd have to write to make an input field that shows a custom error message when a required field isn't populated!&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/usrrname/embed/preview/VYZrMww?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Make it harder for users to screw up
&lt;/h2&gt;

&lt;p&gt;Data entry is a means to an end: collecting data allows teams to improve the product or enable users to acheive their goals.&lt;/p&gt;

&lt;p&gt;The sooner and more validation errors you show, the more punishing it is for the user, the higher the abandonment rate. &lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Next time you're approached to build custom form fields with validation or interactions, consider where your work fits within the org. If you're working on a design system for a product that deals with time, it might be a worthy effort to reinvent a better datepicking experience. But if you're at another run-of-the-mill startup where your work should have been done yesterday, then using tried and true component libraries will save you teeth-gnashing and heartbreak.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Gerardo Rodriguez. &lt;a href="https://cloudfour.com/thinks/progressively-enhanced-form-validation-part-1-html-and-css/#how-to-avoid-invalid-styles-showing-on-page-load" rel="noopener noreferrer"&gt;"Progressively Enhanced Form Validation, Part 1: HTML and CSS"&lt;/a&gt;, CloudFour. August 7, 2023 &lt;br&gt;&lt;br&gt;
Marco Campos. &lt;a href="https://madcampos.dev/talks/tojs-forms/" rel="noopener noreferrer"&gt;"Form validation with (almost) no JS"&lt;/a&gt;. 2023 ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Edward Scott. &lt;a href="https://baymard.com/blog/inline-form-validation" rel="noopener noreferrer"&gt;"Usability Testing of Inline Form Validation: 31% Don’t Have It, 4% Get It Wrong"&lt;/a&gt;, Baymard Institute, Jan 9, 2024. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>javascript</category>
      <category>ux</category>
      <category>showdev</category>
      <category>webcomponents</category>
    </item>
    <item>
      <title>A Mid-career Retrospective of Stores for State Management</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Sat, 21 Dec 2024 14:20:36 +0000</pubDate>
      <link>https://dev.to/jenc/a-mid-career-retrospective-of-stores-for-state-management-4d6l</link>
      <guid>https://dev.to/jenc/a-mid-career-retrospective-of-stores-for-state-management-4d6l</guid>
      <description>&lt;p&gt;Working on a living product after 2 years on design systems and component libraries has inspired reflections on technical choices of startups past.&lt;/p&gt;

&lt;p&gt;In this post, I recount some learnings from using or refusing common, "boring", &lt;a href="https://facebookarchive.github.io/flux/docs/in-depth-overview/" rel="noopener noreferrer"&gt;flux-informed&lt;/a&gt; stores for managing application state.&lt;/p&gt;

&lt;p&gt;🍿 The following is a tale of 3 front end architectures spanning 2018-2024, each with a different stack and state management solution in a regulated domain. &lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  I. The fintech app with several thousand trial users
&lt;/h2&gt;

&lt;p&gt;The Stack: React 14, React-Redux, Yup, Bootstrap, Express, MongoDB, Redis&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.dribbble.com%2Fusers%2F775143%2Fscreenshots%2F6302764%2Fdribbble_bulk_actions_4x.png%3Fresize%3D1600x1200%26vertical%3Dcenter" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.dribbble.com%2Fusers%2F775143%2Fscreenshots%2F6302764%2Fdribbble_bulk_actions_4x.png%3Fresize%3D1600x1200%26vertical%3Dcenter" alt="Screenshot of user interface design by Niclas Ernst for Fintory" width="1600" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.dribbble.com%2Fusers%2F775143%2Fscreenshots%2F6576016%2Fcomparison.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.dribbble.com%2Fusers%2F775143%2Fscreenshots%2F6576016%2Fcomparison.png" alt="Screenshot of a design comp by Niclas Ernst on fund comparison from Dribbble" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My first encounter with sophisticated store usage was React-Redux at a B2B fintech company in 2019. We were using React 11 for dual-module application, its state all sewn together by several &lt;a href="https://react-redux.js.org/api/connect" rel="noopener noreferrer"&gt;&lt;code&gt;connect()&lt;/code&gt;&lt;/a&gt; functions.&lt;sup id="fnref2"&gt;2&lt;/sup&gt; &lt;/p&gt;

&lt;p&gt;Redux solved the need for a view-level source of truth to organize feature-specific state, especially when different parts of the app needed to access information specific to a particular feature. &lt;/p&gt;

&lt;p&gt;Below is a simplistic view of feature states to enable the user to add stocks to portfolios, and contact customer service. &lt;/p&gt;

&lt;p&gt;Each feature has its own reducer, and the store combines them all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmsv01if7fwd65gc1ck7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgmsv01if7fwd65gc1ck7.png" alt="A diagram of a mega-store with feature-oriented reducers" width="800" height="520"&gt;&lt;/a&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="c1"&gt;// store.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configureStore&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;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createStore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="na"&gt;rootReducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;account&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;accountReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;stock&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stockReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;support&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;supportReducer&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;store&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;The plans reflect these business requirements: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the ability to find and add stocks to a portfolio&lt;/li&gt;
&lt;li&gt;to view and compare the progress of purchased stocks&lt;/li&gt;
&lt;li&gt;the ability to privately discuss portfolios with customer support agents&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Just as you wouldn't make an API call to the server every time you need to display data, a store acts as an intermediary space for storing and manipulating presentational data. &lt;code&gt;POST&lt;/code&gt; or &lt;code&gt;DELETE&lt;/code&gt; requests to your server could then be made as needed and responses would also update the store.&lt;/p&gt;

&lt;p&gt;With each app feature having its own store, operations around specific modules are easier to maintain ...as long as we plan carefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  II. Modernizing a surgery prioritization system
&lt;/h2&gt;

&lt;p&gt;The Stack: Angular 8, &lt;a href="https://angular.dev/guide/forms/dynamic-forms" rel="noopener noreferrer"&gt;Dynamic forms&lt;/a&gt;, RxJS, and Angular Material, FHIR Server&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.dribbble.com%2Fuserupload%2F14411927%2Ffile%2Foriginal-b1ec7a14d5ed923ab01675e05fa78b56.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn.dribbble.com%2Fuserupload%2F14411927%2Ffile%2Foriginal-b1ec7a14d5ed923ab01675e05fa78b56.png" alt="A patient intake form" width="800" height="568"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In 2020, I landed at a health tech company whose forte was clinical data storage, but they were trying to make headway on health apps. On one of their projects, my team was tasked to rewrite a legacy .NET surgery prioritization system into an Angular 9 app for a provincial health organization. Their primary users would be hospital admin staff.&lt;/p&gt;

&lt;p&gt;The app would take a combination of answers entered into an 8-part form with 120 fields to assess how soon a patient had to undergo surgery.&lt;/p&gt;

&lt;p&gt;It's not as serious as a medical device, but impactful to patient health outcomes to the extent the app decides who gets a brain scan before whom. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzkt1rpxfmvscvrq55e9c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzkt1rpxfmvscvrq55e9c.jpg" alt="Angular architectural diagram of model view controller" width="800" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Angular’s MVC structure meant service classes performed the passing of state between views. &lt;/p&gt;

&lt;p&gt;Our form was doubly-dynamic: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;fields that were displayed depended on a question-answer model, basically running through &lt;code&gt;Formbuilder&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;subsequent fields that were displayed were dependent on combinations of inputted values of other fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Oh yeah, stakeholders decided that each field had live, &lt;code&gt;oninput&lt;/code&gt; validations that were dependent on the values of other fields.&lt;/p&gt;

&lt;p&gt;Doesn't this sound like it's ripe for a state management solution?&lt;/p&gt;

&lt;p&gt;Well, &lt;strong&gt;we didn't use one&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead, we used observables with RxJS to subscribe to streams of async event data, and used service classes to pass data between views. &lt;/p&gt;

&lt;p&gt;What happens if when only use services to share state?&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;// question.service.ts&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&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;class&lt;/span&gt; &lt;span class="nc"&gt;QuestionService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;getPatientIntakeQuestions&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;QuestionBase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&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="nf"&gt;getPatientProcedureHistory&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;QuestionBase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&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="nf"&gt;getPatientEncounterHistory&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nx"&gt;QuestionBase&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Service classes get longer and longer with custom methods to check for specific conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;State was transient and could only be debugged through inline console logs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where you had conditional adding of fields you'd have to loop through form groups to check whether an &lt;code&gt;AbstractControl&lt;/code&gt; on a particular nested form group's form value had been interacted with.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Conditional rendering and live validations relied on runtime checks against a JSON file with 280 combinations of custom form validation rules.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Business logic starts creeping into controllers, and lines blur for where to put functions that load and update state or cause side-effects.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We abused post and fetch requests to store any state change in the absence of a sink to hold any change in state&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the time, Angular didn't have standalone components and we didn't know about Angular Universal's capabilities of SSR.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; We made multiple API calls sequentially to fetch and populate form questions and previous answers after login, instead of server-side rendering the form statically.&lt;/p&gt;

&lt;p&gt;Due to the length of the form, there was a requirement to save any answers up to point it had been filled. &lt;code&gt;LocalStorage&lt;/code&gt; wasn't an acceptable solution due to the sensitivity of patient health information. It's likely we posted the "auto-saved" versions at idle intervals to a different endpoint.&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;// dynamic-form.component.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamicFormComponent&lt;/span&gt; &lt;span class="kr"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;OnInit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;form&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormGroup&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;formBuilder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;FormBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;questionService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;QuestionService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;ngOnInit&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;form&lt;/span&gt; &lt;span class="o"&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;formBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;patientIntake&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;formBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;questions&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;questionService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPatientIntakeQuestions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;patientProcedureHistory&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;formBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;questions&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;questionService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPatientProcedureHistory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;patientEncounterHistory&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;formBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;questions&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;questionService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPatientEncounterHistory&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="na"&gt;patientSurgeryHistory&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;formBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;questions&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;questionService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPatientSurgeryHistory&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;// and on and on and on...&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="nf"&gt;onInputChange&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// check if a field has been dirtied&lt;/span&gt;
    &lt;span class="c1"&gt;// check if a field is part of a formgroup that should have an instance added&lt;/span&gt;
    &lt;span class="c1"&gt;// invoke live validation for form fields&lt;/span&gt;
    &lt;span class="c1"&gt;// show the errors above form fields&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;ngOnSubmit&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// loop through all forms &lt;/span&gt;
    &lt;span class="c1"&gt;// show the form level validation errors above the form&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// the actual form submission post request&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;formService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;submitForm&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;form&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="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;
    &lt;span class="c1"&gt;// notify user of any response errors&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;App architecture wise, we used some weird hybrid between modules separated by domain and angular's "shared" folder structure. &lt;/p&gt;

&lt;p&gt;Dynamic forms and reactive forms fulfilled the need to render different form data models, but this got tacky as soon as we needed to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;conditionally render nested form groups or fields&lt;/li&gt;
&lt;li&gt;display previously entered information or conditionnally disable previously filled fields&lt;/li&gt;
&lt;li&gt;add another field instance to a form when it's edited or filled&lt;/li&gt;
&lt;li&gt;validate form fields based on the values of other fields&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The disaster that arose from not using a state management solution: longwinded, overly imperative ("if-abc, else if xyz, else if jkl..."), and much harder to maintain and debug code. &lt;/p&gt;

&lt;h2&gt;
  
  
  III. The 7 year old shopping app that was never refactored
&lt;/h2&gt;

&lt;p&gt;The old stack: Nuxt 2, Vue 2, VueX, Tailwind, Vercel, MySQL, Laravel&lt;/p&gt;

&lt;p&gt;The new stack: Nuxt 3, Vue 3, Pinia, Tailwind, Cloudflare pages, MySQL, Laravel&lt;/p&gt;

&lt;p&gt;In the past year, I've been working on migrating Vue 2 and VueX to Vue3 and Pinia in Nuxt 3 for an e-commerce app.&lt;/p&gt;

&lt;p&gt;Imagine an app for boutique knick-knacks starts by hydrating a user store with all the info related to a user whenever they login.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/nuxty-2-shoes-wh7mlv"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;At the beginning, the business only runs in Canada. It's good enough to have everything in one single store. &lt;/p&gt;

&lt;p&gt;Later, this app decides to expand in the US and also sell American goods, and offer specific coupons and promotional discounts to American users.&lt;/p&gt;

&lt;p&gt;Developers decide they need to create an order store to handle the possibility a user may shop in different currencies and payment methods (A contrived example, but not far from what I've seen in the wild).&lt;/p&gt;

&lt;p&gt;The specific project team hurtles to roll out a new store for American orders, but still keep the original one to handle Canadian orders.&lt;/p&gt;

&lt;h3&gt;
  
  
  What could go wrong?
&lt;/h3&gt;

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

&lt;p&gt;The store is split into modules that pertain to domain features, but it has a couple oddities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Similarly named state properties and actions. &lt;code&gt;orderStore().confirmOrders()&lt;/code&gt; and &lt;code&gt;accountStore.purchaseItems()&lt;/code&gt; perform the same function, but they're in different stores. The &lt;code&gt;accountStore&lt;/code&gt; has an &lt;code&gt;items&lt;/code&gt; property that tracks &lt;code&gt;order.data.items&lt;/code&gt; in the cart pre-checkout, but so does the &lt;code&gt;orderStore&lt;/code&gt; for &lt;code&gt;orderStore.items&lt;/code&gt; in the cart, post-checkout!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Duplicated state: while the account store would be populated with user-adjacent info right after login, the order store is updated whenever it's instantiated in American users' sessions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing knowledge loops: Many features of the app still rely on the legacy &lt;code&gt;order.data.items&lt;/code&gt; property, but the &lt;code&gt;orderStore&lt;/code&gt;'s &lt;code&gt;order.items&lt;/code&gt; inventory needs to be displayed in US currency. Without cataloging and updating areas that rely on the old store, the display of the correct cart continues to falter. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Getter abuse: The &lt;code&gt;accountStore&lt;/code&gt; has getters that detail trivial pieces of user state that have been tacked on over time: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;isSubscribedToNewsletter()&lt;/code&gt; is already denoted by the property &lt;code&gt;user.hasSubscribed&lt;/code&gt; returned from the API call. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;isVegetable()&lt;/code&gt; might be helpful for vegetarian users, just as  &lt;code&gt;ordersWithoutPeanuts()&lt;/code&gt; would for all cart items without peanuts, and might have been a feature request quickly implemented to serve those with allergies, but not needed by all users. These two would be better off as computed properties in a &lt;code&gt;useDietaryRestrictions()&lt;/code&gt; composable.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;p&gt;neither store was fully migrated to Vue 3 composition API.&lt;/p&gt;&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;And so, the more features added, the busier the team gets with patching and reconciling state in both stores 😱&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmn3cayxnhnh1mubze7w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkmn3cayxnhnh1mubze7w.jpg" alt="An illustration of daisychain-ed monitors feeding into a laptop" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;
Poorly architected stores can act like daisychained info flows, but not in the way that you'd want!



&lt;p&gt;Eventually, someone has to rip the bandaid off in order to move ahead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;retire the use of &lt;code&gt;accountStore&lt;/code&gt; and relocate Canadian and American orders to show under &lt;code&gt;OrderStore&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the separation is worth longterm sustainability, then 2 stores might co-exist independently with the same data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;we can get the second store to subscribe to the first on load, then on state change, dispatch the actions in the second store.&lt;/li&gt;
&lt;li&gt;OR, we get rid of hydrating the first store and instead hydrate the second on load so we won't have to keep 2 stores' data in sync.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why devs hate stores
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Setup drudgery and boilerplate
&lt;/h3&gt;

&lt;p&gt;The amount of code needed to set up a store, actions, reducers and dispatchers can be overwhelming for newcomers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foen9b2zu0fek5xxhr5pb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foen9b2zu0fek5xxhr5pb.png" alt="Diagram displaying the state management model for a Vue library" width="800" height="241"&gt;&lt;/a&gt;&lt;/p&gt;
VueX itself is particularly verbose with introducing the need to commit mutations before dispatching actions.



&lt;p&gt;To be effective with stores, developers needed profiency with a framework's rendering mechanisms consider the tradeoff of strenuous setup and boilerplate for the benefit of shared state between views.&lt;/p&gt;

&lt;p&gt;Every action had to be created and dispatched via the reducer, and any container view using the store had to be connected to it. I don't recall how many times I wrote &lt;code&gt;mapStateToProps&lt;/code&gt; and &lt;code&gt;mapDispatchToProps&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Redux is rather overkill in today's front end ecosystem if you're building an MVP, and as of late more have been using &lt;a href="https://redux-toolkit.js.org/" rel="noopener noreferrer"&gt;redux-toolkit&lt;/a&gt; or &lt;code&gt;useReducer&lt;/code&gt; to update slices of state.&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Premature optimization leads to higher maintenance efforts
&lt;/h3&gt;

&lt;p&gt;Creating a store for every app feature can lead to unnecessary complexity and coupling. When multiple stores share the same data source, developers have to remember to keep states synchronized or remember which store depends on another. Premature code splitting can confuse teams and make maintenance harder.&lt;/p&gt;

&lt;p&gt;The more stores you have, the more state you have to keep track of!&lt;/p&gt;

&lt;h2&gt;
  
  
  To store or not to store?
&lt;/h2&gt;

&lt;p&gt;Using a store for state management is ideal when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;There is complex, domain-specific logic that needs to be diligently updated as part of a larger product or platform as a user navigates through different views.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It becomes unsustainable to manage state within one or two components and you find that you're passing the same data properties across multiple components. (aka. &lt;a href="https://kentcdodds.com/blog/prop-drilling" rel="noopener noreferrer"&gt;prop-drilling&lt;/a&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There's a need to present or update different types of data across different features of an app, or work with nested deep data structures to update the UI.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If there are multiple dimensions of state to track, a store may not offer enough granularity for defining exact state transitions. It may be worth looking at implementing a state machine (some libraries include &lt;a href="https://xstate.js.org/" rel="noopener noreferrer"&gt;XState&lt;/a&gt; or &lt;a href="https://immerjs.github.io/immer/" rel="noopener noreferrer"&gt;Immer&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;For example, if you need interaction changes through multi-step forms, wizards or like the combinatories of form fields I previously described. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Caveat: I have not had to use above libs yet.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's easy to get to a place where no single pattern is used, but someone somewhere, at some point is going to take issue with whether you use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.patterns.dev/react/presentational-container-pattern/" rel="noopener noreferrer"&gt;Container-component pattern&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;a href="https://www.patterns.dev/vanilla/provider-pattern" rel="noopener noreferrer"&gt;provider&lt;/a&gt; that encapsulates state and actions for a component tree (the basis for Context API)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;an &lt;a href="https://www.patterns.dev/vanilla/observer-pattern" rel="noopener noreferrer"&gt;observer-style&lt;/a&gt; event bus,&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;a &lt;a href="https://www.patterns.dev/vanilla/singleton-pattern" rel="noopener noreferrer"&gt;singleton&lt;/a&gt; that can be updated from anywhere in the app (React Context and Vue composables might be lightweight ways to do this, but I have found that composables are not good replacements for behaviour you'd expect from singletons.)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contemporary state management
&lt;/h2&gt;

&lt;p&gt;Front end frameworks today offer lightweight options for state management.Leveraging out-of-the-box mechanisms will suffice for small to medium-sized apps.&lt;/p&gt;

&lt;p&gt;React's hooks (&lt;code&gt;useState&lt;/code&gt;, &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useContext&lt;/code&gt;) allow for easy encapsulation of reactive business logic. The &lt;a href="https://legacy.reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;Context API&lt;/a&gt; reduces prop drilling by making state accessible at any component level.&lt;/p&gt;

&lt;p&gt;Vue's &lt;code&gt;computed()&lt;/code&gt;, &lt;code&gt;ref()&lt;/code&gt;, and &lt;code&gt;watch()&lt;/code&gt; functions, along with composables, achieve similar results to React hooks and context.&lt;/p&gt;

&lt;p&gt;Newer options like &lt;a href="https://github.com/tc39/proposal-signals" rel="noopener noreferrer"&gt;signals&lt;/a&gt; in Svelte and Angular are also emerging.&lt;/p&gt;

&lt;p&gt;Frameworks aim to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reactively update the UI when data changes&lt;/li&gt;
&lt;li&gt;Subscribe/unsubscribe to data changes&lt;/li&gt;
&lt;li&gt;Manage side effects&lt;/li&gt;
&lt;li&gt;Distinguish between local, shared, and global state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where you need it, it's probably wise to choose a strategy before entropy takes hold.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;The contents of this post are based on my work experiences but I've changed exact details.  ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;React's fundamental principles promoted thinking about data moving through components in a unidirectional matter, and devs could easily control rendering and interactions with methods like &lt;a href="https://legacy.reactjs.org/docs/react-component.html#componentdidmount" rel="noopener noreferrer"&gt;&lt;code&gt;componentDidMount&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://legacy.reactjs.org/docs/react-component.html#componentdidupdate" rel="noopener noreferrer"&gt;&lt;code&gt;componentDidUpdate&lt;/code&gt;&lt;/a&gt;. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Wassim Chegham. "Angular Universal for the Rest of Us" &lt;a href="https://medium.com/google-developer-experts/angular-universal-for-the-rest-of-us-922ca8bac84" rel="noopener noreferrer"&gt;https://medium.com/google-developer-experts/angular-universal-for-the-rest-of-us-922ca8bac84&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Dan Abramov. &lt;a href="https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367" rel="noopener noreferrer"&gt;"You Might Not Need Redux"&lt;/a&gt;, Medium, Sep 19, 2016.  ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>react</category>
      <category>vue</category>
      <category>angular</category>
      <category>showdev</category>
    </item>
    <item>
      <title>The Dual Nature of Seniority in Software Development</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Fri, 12 Jul 2024 16:27:58 +0000</pubDate>
      <link>https://dev.to/jenc/the-dual-nature-of-seniority-in-software-development-1b3j</link>
      <guid>https://dev.to/jenc/the-dual-nature-of-seniority-in-software-development-1b3j</guid>
      <description>&lt;p&gt;Disclaimer: Like everything else coming from me, this thinkpiece is &lt;em&gt;entirely my own opinion&lt;/em&gt; and doesn't reflect the views of my current or previous employers.&lt;/p&gt;

&lt;p&gt;I think it's important to share nuanced perspectives on career and personal growth to combat the stigma of failure, survivorship bias, and other things that make moving up and staying in this industry seem more glamorous or effortless than it is.&lt;/p&gt;

&lt;p&gt;For a mainstream take, check out Gergely Orosz's &lt;a href="https://newsletter.pragmaticengineer.com/p/the-seniority-rollercoaster" rel="noopener noreferrer"&gt;"The Seniority Rollercoaster"&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theory of the Senior Developer
&lt;/h2&gt;

&lt;p&gt;Every company has different expectations for senior developer performance.&lt;sup id="fnref1"&gt;1&lt;/sup&gt; As long as your title has "developer" or "engineer" in it, you're hired to do one of the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;implement features or fix bugs independently with minimal guidance or direction, mentor less experienced devs, review code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;research and solution complex or ambiguous problems your superiors don't have time to tackle &lt;br&gt; &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;2.5 –and by extension, scale your abilities to level up team(s) as part of executing a technical vision or strategy, which may also include offering hiring, training, cross-departmental collaboration, and other leadership functions to alleviate the burden on your superiors.&lt;/p&gt;

&lt;p&gt;Which were you hired to do? What &lt;em&gt;are&lt;/em&gt; you able to do?&lt;/p&gt;

&lt;p&gt;If you're not successful, why the gap between what you're doing and what you're expected to do?&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changed?
&lt;/h2&gt;

&lt;p&gt;When I first started in industry it appeared that expectations of "senior" included all items listed. Senior was a common terminal rung. Over time the career paths for technical leadership seemed to expand to include principle, staff, lead, and architect roles.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;And so it follows, title inflation and varying specialization of roles in different sized companies make expectations and opportunities for growth rather wavy when one embarks on that first rung.&lt;/p&gt;

&lt;p&gt;It's been 10 years Edmund Lau wrote &lt;a href="https://www.effectiveengineer.com/" rel="noopener noreferrer"&gt;Effective Engineer&lt;/a&gt;. During its time of writing, interest rates were lower, allowing tech companies and VCs to borrow money more, and companies spent much more on R&amp;amp;D and competed for talent. The myth of rags-to-riches bootcamp-grad talent and self-taught dropout-unicorns were alive and well.&lt;sup id="fnref3"&gt;3&lt;/sup&gt; Founders lamented over the lack of &lt;a href="https://avichal.com/2011/12/16/focus-on-building-10x-teams-not-on-hiring-10x-developers/" rel="noopener noreferrer"&gt;"10x developers"&lt;/a&gt; hires and offered &lt;a href="https://www.linkedin.com/pulse/googles-20-time-fostering-innovation-employee-robert-baines/" rel="noopener noreferrer"&gt;20% time&lt;/a&gt; to encourage devs to brew features and improvements they didn't know they needed.&lt;/p&gt;

&lt;p&gt;Today, the cost-consciouness fueled by delayed-onset pandemic-rebound cutbacks, and the premature outlook that AI automates dev work have re-prioritized basic efficiences and delivery as senior responsibilities.&lt;/p&gt;

&lt;p&gt;The developer traits celebrated in books that made startups-turned-tech-giants successful, aren't the ones that will help you keep your job, maintain a situational workplace reputation, or get promoted. &lt;/p&gt;

&lt;p&gt;Hiring managers want cross-functional employees that learn fast and execute – not plan, solution, challenge and lead right out of the gate!&lt;/p&gt;

&lt;p&gt;tldr; I think we're seeing shift in preference for Type B "rockstar" archetypes that steadily deliver results without pushing for above-and-beyond, as opposed to Type A "superstars".&lt;sup id="fnref4"&gt;4&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.radicalcandor.com%2Fwp-content%2Fwebp-express%2Fwebp-images%2Fdoc-root%2Fwp-content%2Fuploads%2F2021%2F12%2FScreen-Shot-2021-12-08-at-10.02.56-AM.png.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.radicalcandor.com%2Fwp-content%2Fwebp-express%2Fwebp-images%2Fdoc-root%2Fwp-content%2Fuploads%2F2021%2F12%2FScreen-Shot-2021-12-08-at-10.02.56-AM.png.webp" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  We don't go to work to innovate, lead, or "do agile"
&lt;/h2&gt;

&lt;p&gt;We go to work to ship code expediently to meet business goals.&lt;/p&gt;

&lt;p&gt;Some leaders mind more than others how we achieve those results, and it's best to figure this out at the interview stage or things can get acrimonious.&lt;/p&gt;

&lt;p&gt;Unless you work in big tech company or incubator, the margin and time provided for innovation is slim to none at most workplaces. This isn't a complaint; it's a natural consequence of choosing a career where you trade knowledge work to produce products for money under capitalism.&lt;/p&gt;

&lt;p&gt;In all likelihood, your company already has specialists with specific ideas and priorities to deliver on. Innovation requires weathering trial and error and is the most efficient in small teams in the early market validation stage of a startup. &lt;/p&gt;

&lt;p&gt;By the time they've hired for your position, you don't have time to mess around with wheel reinvention or delivery lubrication. You're there to provide immediate value or fill a gap in capability. People need you in exactly that position for that function to do that exact type of building and maintenance work well.&lt;/p&gt;

&lt;p&gt;Most businesses simply need people who can build well and fast enough without crossing into recommendations of plans or decisions. &lt;/p&gt;

&lt;p&gt;You're more likely to find innovation and technical growth in OSS, community initiatives, or personal projects you share with friends who have more experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  Staring at an undulating plateau
&lt;/h2&gt;

&lt;p&gt;Expecting work to feed your sense of purpose is a tall order. In this economy, it's like expecting a romantic partner to meet all your emotional and intellectual needs.&lt;/p&gt;

&lt;p&gt;You can jump your salary by learning different stacks or frameworks at different kinds of companies, ship working results faster. But more or less, with a track record in doing the same things in different domains, more people, over and over... you receive more of the same challenges.&lt;sup id="fnref5"&gt;5&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fthisisbob.png%26w%3D1920%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Fthisisbob.png%26w%3D1920%26q%3D75" alt="This is bob. Bob crushes bugs and tickets. Bob doesn't go looking for problems to fix. Be like Bob." width="913" height="907"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your time-to-onboard is reduced, but so is the time to dispassionate feature flipping. You become so good in certain areas people would trust you less to do other areas, or they otherwise aren't able to see you go beyond it. Factory-style ticket flipping takes hold.&lt;/p&gt;

&lt;h2&gt;
  
  
  High performance is what your boss considers high performance
&lt;/h2&gt;

&lt;p&gt;Doing what you're asked to well and being a kind human without crossing into the lines of fire is what John Cutler calls a kind of employee that's a &lt;a href="https://cutlefish.substack.com/p/tbm-271-the-biggest-untapped-opportunity" rel="noopener noreferrer"&gt;"skilled pragmatist"&lt;/a&gt;. While Cutler is advocating that this type of employee is the most valuable to engage and under-activated, I consider these types to be living role models I need to learn from more.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The heightened need for professional mindreading
&lt;/h2&gt;

&lt;p&gt;There's an underlying dimension of seniority that I've observed, sets those who are successful and effective beyond senior from those who aren't: &lt;strong&gt;professional mindreading&lt;/strong&gt;. 🦄&lt;/p&gt;

&lt;p&gt;This is an inordinately difficult skill to learn and master.&lt;/p&gt;

&lt;p&gt;When you move up, anticipating what your boss and team needs without actually being told becomes important. They expect to use less time to spell things out for you. You'd hopefully develop a way of working together that complements and augments their approach. That includes keeping up with what changed before and after vacation, messages and non-updates shared in team channels.&lt;/p&gt;

&lt;p&gt;You're expected to read between the lines of what's being said and not said in meetings to evaluate the success and buy-in around a strategy.&lt;/p&gt;

&lt;p&gt;You're expected to read the room, know when to speak up, and when to stay quiet.&lt;/p&gt;

&lt;p&gt;You're expected to know when to take charge, and when to let others take the lead.&lt;/p&gt;

&lt;p&gt;Check-ins and syncs with upper management will provide leads on what matters to them, but how you perform to complement your bosses' strategies without threatening your peers in the day-to-day is the truly insane challenge.&lt;/p&gt;

&lt;p&gt;A lot of success shells down to fit between skillset, mindset and team chemistry. There might be pushback or protest against technical strategy or changes.&lt;sup id="fnref6"&gt;6&lt;/sup&gt; You'll be expected to back them.&lt;/p&gt;

&lt;p&gt;If your manager never had a hire at your level or they're not ready to let someone with their skills take charge, then how you're used to functioning at a previous senior role might come off as overstepping. It can feel puzzling to have some of their skills out-of-the-box, yet not be trusted to do things you could be delivering with your skillset. &lt;/p&gt;

&lt;p&gt;If you don't have the title and you're treated as senior in skill, they'll assess you on this element because promoting you and having you around stakeholders inflects on them. &lt;/p&gt;

&lt;p&gt;People who are effective and impactful may appear popular but it's actually because they're good at mindreading, in addition to building and maintaining relationships around the org. &lt;/p&gt;

&lt;p&gt;Your managers might throw you opportunities to take on staff-level things too, but if you're successful at influencing for desired outcomes, also be wary that your influence doesn't outshine your bosses' or veteran devs.&lt;/p&gt;

&lt;p&gt;Proactivity on the wrong thing or misinterpreting priorities will be seen as a waste of time or inability to follow through on exec priorities. &lt;/p&gt;

&lt;p&gt;I've been primed to defer to authority and distrust my gut through various failure-intolerant environments, but when you are situationally in authority, you experience the consequences of not acting when it's important to.&lt;/p&gt;

&lt;p&gt;And so you're left wondering: &lt;br&gt;
how do you know when to act, and when to step back?&lt;/p&gt;

&lt;p&gt;I don't know, and I'm still learning.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's the way work goes
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5isrhqqrkjims4c0gida.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5isrhqqrkjims4c0gida.gif" alt="Bart Simpson throwing out a square cake with " width="256" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every new role comes with new challenges and expectations. Like dating, staying on track about whether a position and the current business goals would leverage your skills and experience is important.&lt;/p&gt;

&lt;p&gt;It's become less personal over time to connect the dots that I'm a poor fit, and I'd much rather know of that sooner than go through the motions of a PIP or a textbook quiet firing. &lt;/p&gt;

&lt;p&gt;You could on paper, be hitting every goal and OKR, yet hated by management. &lt;/p&gt;

&lt;p&gt;You can be the wrong high-potential hire who has the right experience, yet doesn't fulfill the goals or realize the functions set out.&lt;/p&gt;

&lt;p&gt;You could be set up to fail in landing a role where they give you the title but no actual resource or decision-making power to hire or build out a team. &lt;/p&gt;

&lt;p&gt;Perhaps the way the org is designed doesn't actually require senior capabilities. &lt;/p&gt;

&lt;p&gt;No two senior roles are going to function in the same way. &lt;/p&gt;

&lt;p&gt;The longer I've been in industry, the more humble I have to be.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;Check out &lt;a href="https://progression.fyi/" rel="noopener noreferrer"&gt;Progression.fyi&lt;/a&gt; for examples of publically available career ladders from large tech companies.  ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;See Jorge Fioranelli's &lt;a href="https://www.engineeringladders.com/" rel="noopener noreferrer"&gt;Engineering Ladders&lt;/a&gt; or Sarah Drasner's &lt;a href="https://career-ladders.dev/" rel="noopener noreferrer"&gt;Career Ladders&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;Gergely Orosz, &lt;a href="https://www.youtube.com/watch?v=VpPPHDxR9aM" rel="noopener noreferrer"&gt;"The software engineering industry in 2024: what changed in 2 years, why, and what is next"&lt;/a&gt;, Craft Conference, May 2024. &lt;a href="https://www.cnbc.com/video/2024/04/28/how-working-for-big-tech-lost-dream-job-status.html" rel="noopener noreferrer"&gt;"How working for Big Tech lost its "dream job" status"&lt;/a&gt;, CNBC, April 28, 2024. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;Kim Scott, &lt;a href="https://www.radicalcandor.com/blog/team-growth-and-stability/" rel="noopener noreferrer"&gt;"Balancing Growth and Stability: Why Your Team Needs People On Both Steep and Gradual Growth Trajectories"&lt;/a&gt;, Radical Candor, Jan 01, 2023.  ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;As a front-end focused software dev who's worked with &lt;em&gt;n&lt;/em&gt; JS frameworks, I've noticed I'm encountering the same kinds of problems in different scenarios, people, and constraints. In front end, this looks like migrating from one framework to another, highly available headless CMS e-commerce sites, integrating a third party lib or service that is not available in such-and-such flavor of JS, optimizing configurations to reduce build time, teaching people to unit test, rewriting more variations of the same component, finding new ways to architect state management, revamping or rebranding a website. The worst of it is pixel pushing. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;Ludic Mataroa. &lt;a href="https://ludic.mataroa.blog/blog/your-organization-probably-doesnt-want-to-improve-things/" rel="noopener noreferrer"&gt;"Your organization probably doesn't want to improve things"&lt;/a&gt;, October 8, 2023. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>senior</category>
      <category>career</category>
      <category>productivity</category>
      <category>leadership</category>
    </item>
    <item>
      <title>🛑 Stop resizing your browser: improve testing for responsiveness</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Wed, 22 May 2024 17:42:24 +0000</pubDate>
      <link>https://dev.to/jenc/stop-resizing-your-browser-improve-testing-for-responsiveness-4pbm</link>
      <guid>https://dev.to/jenc/stop-resizing-your-browser-improve-testing-for-responsiveness-4pbm</guid>
      <description>&lt;p&gt;This is an expanded thinkpiece around a lighning talk I did at TorontoJS on April 30, 2024. &lt;a href="https://docs.google.com/presentation/d/1BInG0TXU7DUFTVGaeF6sVQmC9AgTH9OPRW9KfpcwbcE/edit#slide=id.p" rel="noopener noreferrer"&gt;👉🏻 Slides here &lt;/a&gt;&lt;/p&gt;

&lt;p&gt;TLDR; use device mode on browser devtools to accurate emulate the experience on mobile and tablet devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  A familiar story
&lt;/h2&gt;

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

&lt;p&gt;You learn that you have one week to rewrite the frontend for an app and you're given incredibly high fidelity designs. &lt;/p&gt;

&lt;p&gt;After confirming that the designs are mobile-first, you agonize over the breakpoints. &lt;/p&gt;

&lt;p&gt;Then come time for business review or quality desk checks, and you notice your colleagues resizing the browser, then mentioning that things don't look right on tablet or mobile.&lt;/p&gt;

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

&lt;p&gt;This behaviour is &lt;strong&gt;really problematic&lt;/strong&gt; for testing complex UIs.&lt;/p&gt;

&lt;p&gt;When you resize your browser, you're not testing for responsiveness. You're testing the side effect of how a layout looks when you resize a browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser resizing won’t account for 👇🏻
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Viewport height&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This won't match the visual viewport height of the actual devices you'd hope to test on. Chrome on Android and iOS is notorious for having a &lt;a href="https://stackoverflow.com/questions/52848856/100vh-height-when-address-bar-is-shown-chrome-mobile" rel="noopener noreferrer"&gt;slippery viewport height&lt;/a&gt; that doesn't match the understanding of &lt;code&gt;100vh&lt;/code&gt; in CSS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zoom level&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on what the developer wrote into the meta viewport tag, the browser is not going to zoom reliably. &lt;br&gt;
  Zooming changes the width of the visible viewport according to device pixel ratio. For example, zooming in 150% from 1440px on a Macbook Pro with a device pixel ratio of 2 will give you a viewport width of 960px.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Touch event emulation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's are no hover or click events on touch devices.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Orientation&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The transition or initial load in portrait or landscape mode won't be captured.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;User agent strings&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the series of headers that a browser sends to the server with information about the particular device and operating system that the browser is running on.&lt;/p&gt;

&lt;p&gt;So for example, if someone is using Safari on iOS 12 and iPhone 8 and the product specifically has a banner for iOS asking users to use Chrome, you might miss that.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Device pixel ratio&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the relationship between the resolution and the way in software, pixels are drawn on a screen.&lt;/p&gt;
&lt;h2&gt;
  
  
  Between Hardware resolution pixels, Device pixels, and CSS pixels
&lt;/h2&gt;

&lt;p&gt;We commonly think of a pixel as one point of light on a screen or a dot on print, but how it's drawn differs between media.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.w3.org/TR/css3-values/" rel="noopener noreferrer"&gt;W3 CSS3 spec&lt;/a&gt; defines a pixel as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;the visual angle of one pixel on a device with a pixel density of 96dpi and a distance from the reader of an arm's length.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So there's &lt;a href="https://www.w3.org/TR/css3-values/#absolute-lengths" rel="noopener noreferrer"&gt;a physical basis to CSS pixels&lt;/a&gt; but it's unhelpful for our purposes since most top-of-line devices and screens today come in all kinds of resolutions, most of higher density than 96dpi.&lt;/p&gt;

&lt;p&gt;The spec explains there's 2 ways a pixel can be made apparent for us developers:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For a CSS device, these dimensions are anchored either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;by relating the physical units to their physical measurements, or&lt;/li&gt;
&lt;li&gt;by relating the pixel unit to the reference pixel.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alternately, we can think of the CSS pixel as something developers use when developing software for browsers when it comes to defining breakpoints for our viewports.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Lo and behold, the spec defines the &lt;a href="https://www.w3.org/TR/css3-values/#reference-pixel" rel="noopener noreferrer"&gt;device pixel&lt;/a&gt; to refer to the smallest physical unit of a screen". Such pixels are usually constituted by subpixels of red, green and blue. On screens with aspect ratios like 16:9, these pixels are even rectangular!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvelog.velcdn.com%2Fimages%2Faromahyang%2Fpost%2F9d431942-b414-460d-b066-1e718be1603b%2Fimage.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fvelog.velcdn.com%2Fimages%2Faromahyang%2Fpost%2F9d431942-b414-460d-b066-1e718be1603b%2Fimage.png" alt="Device pixel ratio" width="625" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Higher density display screens have way more hardware pixels than the standard 96dpi, so through some magical &lt;a href="https://zpl.fi/image-processing-basics-in-the-browser/" rel="noopener noreferrer"&gt;downsampling&lt;/a&gt; or &lt;a href="https://www.reddit.com/r/oculus/comments/878scp/how_does_supersampling_work_why_does_rendering_a/" rel="noopener noreferrer"&gt;supersampling&lt;/a&gt;, the browser uses device pixel ratio to jam more pixels into 1 CSS pixel.&lt;/p&gt;

&lt;p&gt;And that’s how a device with purportedly 750 x 1336px resolution with a device pixel ratio of 2 can still be targeted by a media query of 320px through 600px!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wy75bp7mjl4quw1a0m2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2wy75bp7mjl4quw1a0m2.png" alt="powerpoint slide with a screenshot of a device with 750 x 1336px resolution" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pro tip: you can actually just type &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio" rel="noopener noreferrer"&gt;&lt;code&gt;window.devicePixelRatio&lt;/code&gt;&lt;/a&gt; into your browser console to get the device pixel ratio of the screen you're on. Web APIs 💖&lt;/p&gt;
&lt;h2&gt;
  
  
  Unintended reflows and re-renders
&lt;/h2&gt;

&lt;p&gt;Narrowing the browser in complex UI with listeners also invokes reflows and re-renders which don’t represent the initial page rendering on device browsers.&lt;/p&gt;

&lt;p&gt;If you’re using components with observers like &lt;code&gt;ResizeObserver&lt;/code&gt;s in carousels, you trigger events and side effects that would otherwise not happen if a user initially loaded the page on a mobile device.&lt;/p&gt;
&lt;h3&gt;
  
  
  Example: Wayfair's Bed and Bath Category page
&lt;/h3&gt;

&lt;p&gt;Here's an example with &lt;a href="https://www.wayfair.com/bed-bath/cat/bed-bath-c215329.html" rel="noopener noreferrer"&gt;Wayfair's Bed and Bath category page&lt;/a&gt; –I have no affiliation or ref-links with them; it's just so I'm not using an example from my paid work where I first discovered this disrepancy.&lt;/p&gt;

&lt;p&gt;In the gif, you'll see that resizing down will lead to the horizontal layout of categories being cut off. However, if you were to load the page in Chrome devtools, you'd see that the nav items are stacked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1endkjwrnk4mx919f18m.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1endkjwrnk4mx919f18m.gif" alt="animated gif of Wayfair's Bed and Bath category page being resized" width="512" height="270"&gt;&lt;/a&gt;&lt;/p&gt;
System specs: Macbook M2 with a resolution of 3024 x 1964 using Chrome 124.0.6367.62 with a Device Pixel Ratio of 2



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60t7t01jb19bxx2ewjoh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60t7t01jb19bxx2ewjoh.png" alt="screenshot of Wayfair's Bed and Bath category page in Chrome devtools" width="512" height="289"&gt;&lt;/a&gt;&lt;/p&gt;
Wayfair's Bed and Bath category page in Chrome devtools on a Pixel 7


&lt;h2&gt;
  
  
  The viewport meta tag
&lt;/h2&gt;

&lt;p&gt;You might recognize this piece of markup found in the document head of websites. It tells the content of the browser to render according to device-width with an initial zoom scale of 1.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You won’t leverage this native capability unless you load your page in the device width in your browser devtools.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp8m8uncl8yokl21d18v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpp8m8uncl8yokl21d18v.png" alt="Screenshot of responsive mode in chrome devtools" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;
Resizing in responsive mode: Inadequate for components with listeners



&lt;h2&gt;
  
  
  Improve Approaches to Testing Responsiveness
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Free tools for dandy visual testing
&lt;/h3&gt;

&lt;p&gt;If you're lazy (or more likely under tight timelines), you can use tools like &lt;a href="https://responsively.app/" rel="noopener noreferrer"&gt;Responsively&lt;/a&gt; or &lt;a href="https://responsivetesttool.com/" rel="noopener noreferrer"&gt;ResponsiveTestTool.com&lt;/a&gt;, or browser extensions like &lt;a href="https://responsiveviewer.org/" rel="noopener noreferrer"&gt;Responsive Viewer&lt;/a&gt; will enable quick visual previews across multiple devices at once.&lt;/p&gt;

&lt;p&gt;I personally like Responsively for visual testing as I develop when I don't have any paid options as I'm able to also capture multiple snapshots across many devices at once.&lt;/p&gt;

&lt;p&gt;Note that while these tools are great for quick visual checks, they won't simulate device CPU or network speeds.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use &lt;a href="https://developer.chrome.com/docs/devtools/device-mode" rel="noopener noreferrer"&gt;device mode&lt;/a&gt; on browser devtools
&lt;/h3&gt;

&lt;p&gt;This is a first-order approximation of device responsiveness. You can simulate different devices, orientations, touch events, and zoom levels, and to some extent, you can even &lt;a href="https://developer.chrome.com/docs/devtools/device-mode#cpu" rel="noopener noreferrer"&gt;throttle the connection&lt;/a&gt; and simulate the &lt;a href="https://developer.chrome.com/docs/devtools/device-mode#throttle" rel="noopener noreferrer"&gt;network and CPU speeds&lt;/a&gt; of older devices.&lt;/p&gt;

&lt;p&gt;Things like this give you an idea of initial page load speeds and interaction to next paint &lt;a href="https://web.dev/articles/inp" rel="noopener noreferrer"&gt;(INP)&lt;/a&gt; (how scripting as a result of async events affect the ease of page interactivity).&lt;/p&gt;

&lt;p&gt;As a rule of thumb, performance should be of concern if it affects user experience or has a negative impact on business cost.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use a device emulation service like BrowserStack or SauceLabs
&lt;/h3&gt;

&lt;p&gt;Nothing is really as good as testing on an actual device running a browser, but as developers we never have enough time to try every device.&lt;/p&gt;

&lt;p&gt;Platforms like &lt;a href="https://browserstack.com" rel="noopener noreferrer"&gt;BrowserStack&lt;/a&gt; or &lt;a href="https://saucelabs.com/" rel="noopener noreferrer"&gt;SauceLabs&lt;/a&gt; offer virtual instances of real devices and browsers for manual and end-to-end testing. Caveat: subscriptions cost money and are on a per-seat basis.&lt;/p&gt;

&lt;p&gt;With Android devices, you can test Chrome on different emulated Android devices with &lt;a href="https://developer.android.com/studio/run/emulator" rel="noopener noreferrer"&gt;Android Studio's emulator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also read about that time I trolled the internet for free device testing in order to &lt;a href="https://www.jenchan.biz/blog/cross-browser-testing-ie11-edge-for-free" rel="noopener noreferrer"&gt;test on IE11&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Automate visual regression testing as part of CI
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2Fstatic%2Fimages%2Fblog%2Fchromatic-diff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2Fstatic%2Fimages%2Fblog%2Fchromatic-diff.png" alt="Screenshot of Chromatic Visual Regression testing software highlighting visual differences between previous code commit and next code commit" width="800" height="702"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2Fstatic%2Fimages%2Fblog%2Fbitbucket-ci-storybook.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fjenchan.biz%2Fstatic%2Fimages%2Fblog%2Fbitbucket-ci-storybook.png" alt="Screenshot of Bitbucket pipeline running Chromatic visual snapshotting tool with successful result" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re working on a design system or component library, then automating your unit tests and having &lt;a href="https://www.jenchan.biz/blog/storybook-and-chromatic-for-visual-regression-testing" rel="noopener noreferrer"&gt;visual regression snapshot tests&lt;/a&gt; chained up in a pipeline gives you full confidence your front end is behaving as it should!&lt;/p&gt;

&lt;p&gt;With this approach, you can catch visual bugs and update the baseline for tests before they hit production. Regardless of whether they're automated, you'll still need a human to write unit tests and to validate what is considered a regression and not just further implementation of design changes.&lt;/p&gt;

&lt;p&gt;Next time you're testing for responsiveness, remember that resizing your browser is not the same as testing on a mobile device.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;There's a history of thought on this topic due to the freakout around responsive design introduced by the iPhone 3. I really recommend reading &lt;a href="https://www.quirksmode.org/blog/archives/2010/04/a_pixel_is_not.html" rel="noopener noreferrer"&gt;"A pixel is not a pixel is not a pixel"&lt;/a&gt; and &lt;a href="https://www.quirksmode.org/mobile/viewports.html" rel="noopener noreferrer"&gt;"A tale of two viewports"&lt;/a&gt; by Peter Paul Koch. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;This &lt;a href="https://www.danrodney.com/blog/2x-1x-demo/" rel="noopener noreferrer"&gt;demo by Dan Rodney&lt;/a&gt; is one of the best I've seen on the impact of device pixel ratio on image rendering. In case you're worried, today it's possible to export at 1-3x density from Figma and to use the &lt;code&gt;srcset&lt;/code&gt; attribute in HTML to serve the right image to the right device. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>responsivedesign</category>
      <category>testing</category>
      <category>frontend</category>
      <category>performance</category>
    </item>
    <item>
      <title>Error monitoring and bug triage: Whose job is it?</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Sat, 02 Mar 2024 16:23:42 +0000</pubDate>
      <link>https://dev.to/jenc/error-monitoring-and-bug-triage-whose-job-is-it-33fc</link>
      <guid>https://dev.to/jenc/error-monitoring-and-bug-triage-whose-job-is-it-33fc</guid>
      <description>&lt;p&gt;I first learned issue triage as a support drone working on a product that had 300 municipal customers. Several times a day, I'd hop on a call with a CPO to (hassle him to) make decisions on escalated customer issues and relay the handoff or feedback to and from developers. Later these skills translated to subsequent dev roles where discussing triage and related issues seemed a lightning rod depending on the environment. At that point, my role as not-real-dev and troubleshooter was to be a buffer between devs and PMs. &lt;/p&gt;

&lt;p&gt;With the continued tightening of engineering budgets in 2024, most workplaces are likely expected to do more with less, so &lt;a href="https://www.jenchan.biz/blog/bug-triage" rel="noopener noreferrer"&gt;triage and prioritization&lt;/a&gt; is a great skill to have in your back pocket that AI won't get right yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  Competing incentives 
&lt;/h2&gt;

&lt;p&gt;From a non-engineering perspective, any maintenance or tech debt can be thought of as  nuisance, taking your most expensive resource from focusing on activities that generate revenue–building and shipping new features. &lt;/p&gt;

&lt;p&gt;I'm also not surprised if developers don't eagerly volunteer to bug sweep on Sentry or resolve customer support fires. &lt;/p&gt;

&lt;p&gt;Maintenance is unsexy; it doesn't directly support value delivery. You don't get the same payoff from working all night to ship a feature compared to shipping a hotfix for something found the day after release. &lt;/p&gt;

&lt;p&gt;I haven't seen anyone celebrated or thanked for reducing error logs, patching a bug or backfilling a feature a customer might be losing their mind on.&lt;/p&gt;

&lt;p&gt;And yet, both log monitoring and bug triage are crucial to maintaining the ability to ship fast and resolve errors quickly. &lt;/p&gt;

&lt;h2&gt;
  
  
  Someone's gotta do it
&lt;/h2&gt;

&lt;p&gt;If someone is already doing triage on a somewhat-regular basis, it's probably a product manager, engineering manager, QA or lead dev, or a support person who's combing through like a lone wolf and making independent decisions or chasing down the information they need to make escalations. &lt;/p&gt;

&lt;p&gt;If the manager isn't technical, often some unlucky dev gets assigned a ticket they have no idea how to fix or reproduce, and hopefully the originary implementing devs are around to advise why they wrote the code the way they did.&lt;/p&gt;

&lt;p&gt;If there's an on-call or rotating support role on a team this might be handled periodically with no shared context or clear decision maker. This is still better than nothing, but the impact ends at delegation and the loop never gets completed on how certain bugs could be prioritized or delegated based on domain or sprint and quarterly goals.&lt;/p&gt;

&lt;p&gt;Ideally a core group composed of product, dev, QA leaders meet frequently to prioritize bugs surfaced by support and dev. Unless a company is highly collaborative and mature in agile processes, this doesn't happen as often as it should.&lt;/p&gt;

&lt;p&gt;The best triage experiences I had involve a max of 4 people, with SMEs who have done the bug reproduction or understand the backlog item to evaluate effort. The SME likely needs to be fairly confident with their craft or know who worked on a particular domain of the codebase to offer estimates and severity, in addition to considering the impact of taking on the fix to their current and upcoming work.&lt;/p&gt;

&lt;p&gt;Triage is only productive if the group agrees on a decision maker. Engineers who waffle on details too long need to be reminded that ultimately all are there to understand the viability and effort of a fix. Decision makers also need to be unafraid of hear out contrasting opinions and make quick difficult decisions given tradeoffs and constrained time. Triage meetings that go overtime because managers won't commit to decisions out of fear. In that situation it might move things along to ask a question as if followed up with your "recommendation" to give them an easy choice, then move onto the next thing instead of keeping them around to hem and haw. &lt;/p&gt;

&lt;h2&gt;
  
  
  The cycle of neglect
&lt;/h2&gt;

&lt;p&gt;Some version of the following happens when bugs and regressions aren't triaged with input from different SMEs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Managers delegate bugs to fill capacity in sprints to the brim, assuming an ever-present "nice-to-have" train of bugs devs could grab from when all their sprint work is done.&lt;br&gt;
 &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Developers are assigned bugs with little-to-no context or severity, and no one is assigned to reproduce or investigate the bug.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;Every bug from previous releases get indiscriminately spread out across current or upcoming sprints. Cosmetic, pixel-pushing changes are mislabelled "medium" priority while bugs that are actually feature requests get labelled as high deathmarch feature-backfills.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;Developers get pulled off sprint work to debug and bandaid the latest production fire.  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;The team wastes time on low value fixes; the backlog keeps growing ends up frustrating much higher level execs who wonder why velocity and burndown never matches issue input since they have no visibility over the cause of a bloated backlog in the first place. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;Everyone contently ignores error logs and focuses on next urgent fires until some other leader escalates it to an exec, who is inevitably going to wonder "Why didn't we catch this issue sooner?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;No attempt is made to plan for the future because you're already off course from pre-existing sprint goals that should have been done last quarter, and developers are already overwhelmed as is. All energy for improvement is exhausted by too many incoming tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt; &lt;/p&gt;
&lt;center&gt;👇🏻&lt;/center&gt;

&lt;ul&gt;
&lt;li&gt;The cycle repeats (and some uptight dev blogs about it)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Process introduction as lightning rod
&lt;/h2&gt;

&lt;p&gt;Bug triage can be a touchy process to introduce, especially if there are unclear roles and many titles. If meetings are more often used to re-announce already-made decisions and work culture isn't particularly collaborative, introducing triage isn't going to bode well.&lt;/p&gt;

&lt;p&gt;If no one has been doing it, it's highly likely every party believes someone else should be responsible for triage.&lt;/p&gt;

&lt;p&gt;If you start doing it but no one knows, then you cheat yourself out of the recognition of technical leadership for helping your team become more effective. &lt;/p&gt;

&lt;p&gt;If you start asking managers to participate you might get panicked faces or refusal to be a part of a new process someone else came up with. &lt;/p&gt;

&lt;p&gt;If you start (god forbid) showing people how to do it, the people who should be doing it might feel like they're being told how to do their job! &lt;/p&gt;

&lt;p&gt;In all likelihood, people already recognize gaps in planning and prioritizing, but are too overwhelmed to fit &lt;em&gt;just one more meeting&lt;/em&gt; in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;On large cross functional teams with no clear decision maker, triage is often skipped or delegated away in the interest of keeping the greatest number of people working on sprint goals. This seems like a missed opportunity for preventing future fires and improving incident response.&lt;/p&gt;

&lt;p&gt;Any tools or systems set up for it is only helpful for firefighting or customer support in as much as the right errors are handled and captured. &lt;/p&gt;

&lt;p&gt;Without rostered or routine patrols of logs we don't have a picture of what's not working well, and how it fits within org goals.&lt;/p&gt;

&lt;p&gt;If you set up logging or process and never check it, it's not being leveraged fully as part of quality or incident management. &lt;/p&gt;

&lt;p&gt;Weigh out the fights you're unintentionally starting just by exercising your knowledge on process efficiency. Process improvement is of less concern if a company is prone to behave reactively.&lt;/p&gt;

</description>
      <category>bugs</category>
      <category>agile</category>
      <category>career</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Dissecting the hell that is Jest setup with ESM and Typescript</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Sun, 31 Dec 2023 19:21:59 +0000</pubDate>
      <link>https://dev.to/jenc/dissecting-the-hell-that-is-jest-setup-with-esm-and-typescript-2bcp</link>
      <guid>https://dev.to/jenc/dissecting-the-hell-that-is-jest-setup-with-esm-and-typescript-2bcp</guid>
      <description>&lt;h2&gt;
  
  
  Jest: Not So Delightful Anymore
&lt;/h2&gt;

&lt;p&gt;Several months ago, I had the hardest time setting up Jest with a React 18 Typescript project as part of a spike to help teams who wanted to use Jest to test web components that were built with ESM dependencies.&lt;/p&gt;

&lt;p&gt;As I polled my peers working on React products, many mentioned they had moved over to Vitest.&lt;/p&gt;

&lt;p&gt;The outlook for Jest leading up to this moment hasn't been great. In 2022, one of Jest's OSS maintainers mentioned &lt;a href="https://www.reddit.com/r/javascript/comments/sitf3e/nobody_at_facebook_has_worked_on_jest_for_years/" rel="noopener noreferrer"&gt;"no one at Facebook... worked on Jest for years"&lt;/a&gt;. The project was subsequently &lt;a href="https://engineering.fb.com/2022/05/11/open-source/jest-openjs-foundation/" rel="noopener noreferrer"&gt;transferred to the OpenJS foundation&lt;/a&gt; for maintenance and development has slowly fizzled since.   &lt;/p&gt;

&lt;p&gt;As of December 2023, Jest support for esmodules is &lt;a href="https://jestjs.io/docs/ecmascript-modules" rel="noopener noreferrer"&gt;still experimental&lt;/a&gt; due to its unfortunate reliance on node's &lt;a href="https://nodejs.org/docs/latest/api/vm.html#class-vmmodule" rel="noopener noreferrer"&gt;vm module&lt;/a&gt; for test isolation.&lt;/p&gt;

&lt;p&gt;If you discover yourself at a large company where tech isn't a core competency, you might realize there's a world where many revenue-generating products are still chugging along, one-Jenga-move-from-collapse, on ancient versions of Angular and React. A significant amount of time working as a dev at such a company will be spent helping teams figure out how to integrate extremely outdated frameworks and tools with modern web things.&lt;/p&gt;

&lt;p&gt;If I'm appropriately tactful, I might eventually persuade them to migrate and upgrade their already end-of-life-d framework if they realize the diminishing returns on clinging to legacy technical choices that only compounds in friction to new feature delivery... &lt;/p&gt;

&lt;p&gt;More often, organizational politics make it such that you have to just make something irrelevantly old work with something rather new (and possibly immature) because upgrading or changing tools isn't an imaginable option for said team.&lt;/p&gt;

&lt;p&gt;Investigating how last 2 versions of Create React App (deprecated) and Jest could seamlessly import and test ESM web component library was the laughable situation I found myself in.&lt;/p&gt;

&lt;p&gt;Oh yeah here's the &lt;a href="https://stackblitz.com/~/github.com/usrrname/glowing-broccoli" rel="noopener noreferrer"&gt;sample StackBlitz&lt;/a&gt; if you don't feel like reading.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overcoming Configuration Hell
&lt;/h2&gt;

&lt;p&gt;Rotating through some hell combinations of &lt;a href="https://kulshekhar.github.io/ts-jest/docs/guides/esm-support/" rel="noopener noreferrer"&gt;&lt;code&gt;ts-jest&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://www.npmjs.com/package/ts-node" rel="noopener noreferrer"&gt;&lt;code&gt;ts-node&lt;/code&gt;&lt;/a&gt;, and &lt;a href="https://babeljs.io/" rel="noopener noreferrer"&gt;&lt;code&gt;babel&lt;/code&gt;&lt;/a&gt; plugins as recommended by different official documentation sources, I rehashed the following to get it working: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use the &lt;a href="https://gist.github.com/danpetitt/37f5c966886f54e457ece4b08d66e404#jest" rel="noopener noreferrer"&gt;&lt;code&gt;node --experimental-vm-modules&lt;/code&gt;&lt;/a&gt; when running the jest as part of your &lt;code&gt;package.json&lt;/code&gt; &lt;code&gt;test&lt;/code&gt; script due to Jest's API relying on a node vm API that's still in &lt;a href="https://github.com/nodejs/node/issues/37648" rel="noopener noreferrer"&gt;experimental stage&lt;/a&gt; for esm scripts.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// package.json
"scripts": {
  "test": "node --experimental-vm-modules node_modules/.bin/jest"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;We have to tell Node our project uses ESM, so we add &lt;code&gt;"type": "module"&lt;/code&gt; to &lt;code&gt;package.json&lt;/code&gt; just to support ESM import statements. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This leads to a &lt;a href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c#how-can-i-move-my-commonjs-project-to-esm" rel="noopener noreferrer"&gt;cascade of necessary changes&lt;/a&gt; to turn a CommonJS project to ESM: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;changing all CommonJS files with &lt;code&gt;requires("module-name")&lt;/code&gt; statements to &lt;code&gt;import x from 'module-name'&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;changing &lt;code&gt;module.exports&lt;/code&gt; to &lt;code&gt;export default {}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;change any &lt;code&gt;.js&lt;/code&gt; file extensions to &lt;code&gt;.cjs&lt;/code&gt; or ESM config files to &lt;code&gt;.mts&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Should be fine right? ...Not if you already have a variety of components and views importing from CJS and ESM dependencies.&lt;/p&gt;

&lt;p&gt;At this point you have to decide whether you will isolate the folders that use ESM from the rest of the project to create a different package which can be imported into your CJS project, or if you will just convert the entire project to ESM.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Surprise! Whenever you have &lt;code&gt;package.json&lt;/code&gt; type set to &lt;code&gt;module&lt;/code&gt;, you'll encounter many errors:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  ReferenceError: module is not defined in ES module scope
  This file is being treated as an ES module because it has a '.js' file extension and '/Users/username/projectname/package.json' contains "type": "module". To treat it as a CommonJS script, rename it to use the '.cjs' file extension.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This next one is easy; just rename &lt;code&gt;jest.config.js&lt;/code&gt; to &lt;code&gt;jest.config.cjs&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add Typescript support.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Install &lt;code&gt;ts-node&lt;/code&gt; and &lt;code&gt;ts-jest&lt;/code&gt; to help with the execution and transformation of &lt;code&gt;.ts&lt;/code&gt; files to &lt;code&gt;.js&lt;/code&gt; files before running tests.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx npx ts-jest config:init&lt;/code&gt; creates a &lt;code&gt;jest.config.js&lt;/code&gt; file which you can configure to your liking.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// jest.config.cjs
module.exports = {
  testEnvironment: 'node',
  transform: {
    '^.+\\.ts$': 'ts-jest',
  },
  transformIgnorePatterns: ['node_modules'],
  testMatch: ['**/*.test.ts'],
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using &lt;code&gt;ts-jest&lt;/code&gt; and the &lt;code&gt;transform&lt;/code&gt; property in &lt;code&gt;jest.config.ts&lt;/code&gt; will help with recognizing &lt;code&gt;.ts&lt;/code&gt; or &lt;code&gt;.tsx&lt;/code&gt; test files. &lt;/p&gt;

&lt;p&gt;But &lt;strong&gt;don't&lt;/strong&gt; use the &lt;code&gt;transform&lt;/code&gt; property if you're also using a &lt;code&gt;ts-jest&lt;/code&gt; preset! (See the &lt;a href="https://kulshekhar.github.io/ts-jest/docs/getting-started/presets/#the-presets" rel="noopener noreferrer"&gt;module writer's disclaimer&lt;/a&gt; on that)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;But wait, there's more! Our test-runner complains about the &lt;code&gt;import/export&lt;/code&gt; syntax:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  SyntaxError: Cannot use import statement outside a module
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Jest (with some support from Babel) transforms all files to CommonJS before running tests; it doesn't support &lt;code&gt;import/export&lt;/code&gt; statements from ES2015.&lt;/p&gt;

&lt;p&gt;This is especially infuriating if you import a component or util function that has a direct dependency on an ESM export. &lt;br&gt;
You literally will be blocked on running Jest tests on that particular component. (Even if you already configured your jest config to exclude &lt;code&gt;node_modules&lt;/code&gt;). &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you by now, have decided to convert the entire project to ESM, you'll need to use &lt;code&gt;babel&lt;/code&gt; to transform all &lt;code&gt;.js&lt;/code&gt; files to &lt;code&gt;.cjs&lt;/code&gt; files before running tests. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you haven't already, you'll want to use a &lt;code&gt;babel.config.js&lt;/code&gt; or &lt;code&gt;babel.config.json&lt;/code&gt; to help with recognizing React &lt;code&gt;.jsx&lt;/code&gt; files and transforming them to &lt;code&gt;.js&lt;/code&gt; files before running tests.&lt;/p&gt;

&lt;p&gt;Our friend is &lt;a href="https://babeljs.io/docs/babel-preset-env" rel="noopener noreferrer"&gt;&lt;code&gt;@babel/preset-env&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// babel.config.js
module.exports = {
  presets: [
    [
      '@babel/preset-env',
      '@babel/preset-react'
    ]
  ]
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I've personally found &lt;code&gt;babel/preset-typescript&lt;/code&gt; to have no impact on Typescript transformation. &lt;/p&gt;

&lt;p&gt;Side note: the maintenance-abandoned nature of CRA means installing &lt;code&gt;@babel/plugin-proposal-private-property-in-object&lt;/code&gt; as a devDependency or plugin to be helpful for reducing console noise.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;At the end of the day, you catch yourself needing to decide when linting, transforming need to happen before tests run. I settled on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;typechecking it first with &lt;code&gt;tsc&lt;/code&gt; , OR using &lt;code&gt;ts-jest&lt;/code&gt; to ensure the types are correct as tests run. &lt;/li&gt;
&lt;li&gt;transpiling with babel to CJS before running tests &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If all else fails, I also had some success downgrading to Jest 27.x or 28.x instead of bashing my head on bloated config and confusing doc on 29.x. &lt;/p&gt;

&lt;h2&gt;
  
  
  Related Reading
&lt;/h2&gt;

&lt;p&gt;BigMan73. &lt;a href="https://stackoverflow.com/questions/68956636/how-to-use-esm-tests-with-jest" rel="noopener noreferrer"&gt;"Answer: Jest Typescript with ES Module in node_modules error - Must use import to load ES Module"&lt;/a&gt;, StackOverflow. Jan 4, 2023&lt;/p&gt;

&lt;p&gt;r/node. &lt;a href="https://www.reddit.com/r/node/comments/14rg9ym/esm_not_gaining_traction_in_backend_node/" rel="noopener noreferrer"&gt;"Jest Not Getting Support in Backend Node?"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/75001235" rel="noopener noreferrer"&gt;"Jest Typescript with ES Module in node_modules error - Must use import to load ES Module"&lt;/a&gt;, StackOverflow, &lt;/p&gt;

&lt;p&gt;SimenB. &lt;a href="https://github.com/jestjs/jest/issues/11167" rel="noopener noreferrer"&gt;"Support ESM versions of all pluggable modules"&lt;/a&gt; Mar 7, 2021 &lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/sindresorhus" rel="noopener noreferrer"&gt;Sindre Sorhus&lt;/a&gt;, &lt;a href="https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c" rel="noopener noreferrer"&gt;"Pure ESM package"&lt;/a&gt;, 2023&lt;/p&gt;

</description>
      <category>testing</category>
      <category>jest</category>
      <category>typescript</category>
      <category>react</category>
    </item>
    <item>
      <title>Crossing the senior chasm</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Thu, 26 Oct 2023 20:39:39 +0000</pubDate>
      <link>https://dev.to/jenc/crossing-the-senior-chasm-5h0d</link>
      <guid>https://dev.to/jenc/crossing-the-senior-chasm-5h0d</guid>
      <description>&lt;p&gt;Until you're working well and closely with senior devs, you might not know you're ready to take on senior or staff level challenges!&lt;/p&gt;

&lt;p&gt;For some, attaining the title has become a holy grail pre-occupation as they trudge through workplaces with uncharted growth paths and immature leadership. For others, that title is just a prefix in a long career of building things.[^1]&lt;/p&gt;

&lt;p&gt;The skill range, compensation and responsibilities around seniority is wide across industry; it's normal to feel perplexed and never ready enough.&lt;/p&gt;

&lt;p&gt;Since you're reading, I imagine you've been contemplating a move up. Here are some ideas after bashing my head since 2020.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes a senior dev?
&lt;/h2&gt;

&lt;p&gt;There's many ways to be senior. Check out Neil &lt;a href="https://neilonsoftware.com/underappreciated-software-developers/" rel="noopener noreferrer"&gt;"12 types of under-appreciated software developers"&lt;/a&gt; for traits you might not be aware of.&lt;/p&gt;

&lt;p&gt;If we defer to Sara Drasner's &lt;a href="https://career-ladders.dev/engineering#senior-engineer" rel="noopener noreferrer"&gt;Career Ladders&lt;/a&gt;, a senior engineer "[masters] how effective they can be as an individual contributor"&lt;/p&gt;

&lt;p&gt;What about more specialized kinds of senior? Like an architect or staff engineer?&lt;br&gt;
Will Larsen describes &lt;a href="https://staffeng.com/guides/staff-archetypes/" rel="noopener noreferrer"&gt;different archetypes&lt;/a&gt; of staff engineer.&lt;/p&gt;

&lt;p&gt;Which path interests you? Why does it matter to you?&lt;/p&gt;

&lt;p&gt;What kind of impact do you want to have on a team and by extension, your community and industry?&lt;/p&gt;

&lt;h2&gt;
  
  
  How do I get promoted?
&lt;/h2&gt;

&lt;p&gt;To be promoted internally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you need to demonstrate you get shit done (i.e. are able to complete features independently or guide a team towards delivery)&lt;/li&gt;
&lt;li&gt;you've discussed a growth path with clear goals with your manager&lt;/li&gt;
&lt;li&gt;you need advocates who will vouch for your promotion and already see your potential&lt;/li&gt;
&lt;li&gt;your boss can justify paying you more to his boss&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a lot of to line up. Wouldn't it make more sense to discover your own leadership style at a new job?&lt;/p&gt;

&lt;p&gt;Most companies that need to scale or build product fast and will opt to hire people externally instead of internally promoting whomever has been taking on extra work. If you've been struggling to get a promotion, &lt;em&gt;this is your chance&lt;/em&gt;.&lt;br&gt;
Every new job is an opportunity to rebuild the perception of you at work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Ftriangle.jpeg%26w%3D1080%26q%3D75" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.jenchan.biz%2F_next%2Fimage%3Furl%3D%252Fstatic%252Fimages%252Fblog%252Ftriangle.jpeg%26w%3D1080%26q%3D75" alt="Pick 2 triangle meme" width="500" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;There is no dream job. Which bowl of shit do you wanna eat?&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What about seniority appeals to you as a next step?
&lt;/h2&gt;

&lt;p&gt;Beyond the apparent credibility of the title, and setting yourself up for better paid opportunities, what are your inherent beliefs about the position?[^1]&lt;/p&gt;

&lt;p&gt;Is it a step to another destination?&lt;/p&gt;

&lt;p&gt;If you're interested for the paybump and influence, there's easier paths to do that without needing to juggle the sometimes-escalating fuckery of balancing business needs and personalities with learning everchanging technologies to build software.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signs you're not ready to make the leap
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;[ ] you're bothered by peers' achievements.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you feel it's important be right instead of creating consensus in a situations of disagreement&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you prefer to work alone on a project or worry that someone may steal your ideas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you can't bear the possibility of being wrong publicly.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you don't like to spend time connecting with people to build software.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you don't think it's your job to improve things for your team or make their lives easier&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you don't particularly enjoy learning about emerging technologies or practices&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you're skilled at writing code or learning things fast, but don't like explaining how you do something more than once&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;[ ] you're not interested in helping less experienced developers grow.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are too many rude, morale-draining, fear-inducing technical leaders, some of which aren't worth their weight in experience or competency based on how poorly they treat others.&lt;/p&gt;

&lt;h3&gt;
  
  
  Let's improve the world by not being like that.
&lt;/h3&gt;

&lt;p&gt;Technical rigour is a given, but communication and working with people distinctly unlike you will be the most challenging to navigate.&lt;/p&gt;

&lt;p&gt;With that out of the way, let's talk look at how to promote yourself, mentally and occupationally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a track record of Getting Shit Done
&lt;/h2&gt;

&lt;p&gt;Do what you say, and say what you do. Ankita Kulkarni mentions this in &lt;a href="https://www.growthfor90days.com/" rel="noopener noreferrer"&gt;"The Leaders Playbook: First 90 Days"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Some ways to demonstrate accountability besides shipping features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;following up promptly and providing status updates to the right people&lt;/li&gt;
&lt;li&gt;going to meetings prepared with agenda, acknowledge viewpoints and summarize takeaways&lt;/li&gt;
&lt;li&gt;surface risks and tradeoffs while letting people make the final call&lt;/li&gt;
&lt;li&gt;helping leaders make decisions when they're unsure&lt;/li&gt;
&lt;li&gt;mention priority and provide nuanced but concise estimations given different scenarios&lt;/li&gt;
&lt;li&gt;letting people know you broke &lt;code&gt;main&lt;/code&gt; and you're fixing it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Take projects to the finish line
&lt;/h2&gt;

&lt;p&gt;If you can write features independently but are looking to gain more responsibility, you might start getting involved in conversations that involve different decision makers across the business.&lt;/p&gt;

&lt;p&gt;Or you might be onboarding the rest of a team to implement a solution from a senior developer, unblocking folks so they can deliver their features or fixes on time.&lt;/p&gt;

&lt;p&gt;At some point, you'll develop opinions about how to build better, safer, stabler software.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.radicalcandor.com%2Fwp-content%2Fwebp-express%2Fwebp-images%2Fdoc-root%2Fwp-content%2Fuploads%2F2021%2F07%2FScreen-Shot-2021-07-29-at-11.23.50-AM.png.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.radicalcandor.com%2Fwp-content%2Fwebp-express%2Fwebp-images%2Fdoc-root%2Fwp-content%2Fuploads%2F2021%2F07%2FScreen-Shot-2021-07-29-at-11.23.50-AM.png.webp" alt="Getting shit done diagram by Kim Scott" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Above: Kim Scott's &lt;a href="https://www.radicalcandor.com/podcast/s4-e2-the-gsd-wheel/" rel="noopener noreferrer"&gt;"Getting Shit Done"&lt;/a&gt; circle&lt;/p&gt;

&lt;p&gt;The key function of a senior is to force-multiply and support those with less experience while delivering value consistently. Some passion for craft helps, too.&lt;/p&gt;

&lt;p&gt;When the business needs it, you'll be the firefighter or code janitor for anything that goes wrong on the codebase. Learning fast to find solutions, unblocking and coaching others, descoping features so folks know what to work on, reminding people to merge their features sooner than later, exploring solutions on shaky ground become common responsibilities in seniority.&lt;/p&gt;

&lt;p&gt;It might have seemed heroic when you weren't senior to stay late to hotfix a broken release. After that, it's just part of your job. &lt;br&gt;
Note: I don't believe it's a requirement to work overtime, but such tasks often fall onto senior devs due to mismanagement.&lt;/p&gt;

&lt;p&gt;If you've done this, well there's a few more more lines on your resume! ✨&lt;/p&gt;

&lt;h2&gt;
  
  
  Adjust your signal to your audience
&lt;/h2&gt;

&lt;p&gt;Being able to talk to contributors and leaders from other departments will be important in building trust and improving your visibility across your organization. Outside of talking to developers, you'll need to reframe technical subjects and concerns in laypersons terms with analogies and metaphors.&lt;/p&gt;

&lt;p&gt;For example, a product manager doesn't need to know the exact mechanics of what is causing a critical error, but more likely, which error, how severe and feasible it is to be fixed, and who might be best to assign it to.&lt;/p&gt;

&lt;p&gt;A tech executive might still code and know exactly what you're talking about if you describe all facets of a problem, but won't have time to read 3 paragraphs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build relationships with other senior folk
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37t45pp55u54gy4o1qxa.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37t45pp55u54gy4o1qxa.jpeg" alt="The difference between coach, sponsor and mentor according to Addy Osmani" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Assuming you have been doing this for a while (couple years, or even 10!) and you're genuine in your attempts to help others, along the way, you'll meet people above or below or adjacent to your levelling that will inspire, encourage and guide you, or simply be there to help in your darkest hour. Some might even sing your praises.&lt;/p&gt;

&lt;p&gt;Or by engaging with online communities you might encounter regulars who you end up getting to know, and they may offer on-the-fly advice or perspective on situations at work.&lt;/p&gt;

&lt;p&gt;When you meet develop such relationships, ensure you mutually agree on how they can help you, and your own goals.&lt;br&gt;
The formality and structure may vary.&lt;/p&gt;

&lt;p&gt;I've even found as of late, having conversations every six months or a year with someone I used to work with, or with more experience, has been helpful for my own growth.&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't screw up with mentors, coaches or sponsors
&lt;/h3&gt;

&lt;p&gt;In all likelihood you're a respectful human so this section is more for well, just in case.&lt;/p&gt;

&lt;p&gt;Jobs and leads don't show up on a plate just, especially not just because you know someone, intern or volunteer for them.&lt;/p&gt;

&lt;p&gt;The one who is your supporter may be sticking their neck out for you by recommending, referring or advocating for your promotion.&lt;/p&gt;

&lt;p&gt;So, don't be late to a referral-offered interview, or drop the ball on practicing a presentation you agreed to give, etc.&lt;/p&gt;

&lt;p&gt;Performing poorly or being unprofessional will lower the credibility of the sponsor recommending you, thus making it less likely they'd recommend you in the future as it inflects poorly on them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reflect and process feedback
&lt;/h2&gt;

&lt;p&gt;Working closely with a team over time, you'll notice gaps between on-the-ground work and management vision. You're inevitably going to encounter disagreements on approach.&lt;/p&gt;

&lt;p&gt;If you've gained the trust of your team and stakeholders, they'll be turning to you for help, clarification, for decisions, or just to vent. People all kinds of experience will also be approaching you with opportunities. You may also find yourself manipulated, pressured or gaslit to fulfill different expectations and agendas.&lt;/p&gt;

&lt;p&gt;Higher communication overhead from increasing seniority requires increased self management: the ability to maintain professionalism and calm in the face of doom, to identify and reduce personal frustration, or perhaps to not let as many different opinions and reach-outs clog up your work time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hbr.org/2016/01/want-to-be-an-outstanding-leader-keep-a-journal" rel="noopener noreferrer"&gt;Journaling&lt;/a&gt; to document and understand thought patterns and sus out politics without letting stress become a venting chamber to those close to you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a track record of going Above and Beyond
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Improve delivery or business outcomes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.businessballs.com%2Flocal%2Fpix%2Fbusinessballs%2Ftreeswing%2Ftree-swing-s-hogh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.businessballs.com%2Flocal%2Fpix%2Fbusinessballs%2Ftreeswing%2Ftree-swing-s-hogh.jpg" alt="Alan Chapman's original tireswing drawing" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your ability to learn a business domain and translate that to an agreeable implementation for a team while ensuring they're productive will be your best asset. This might involve teaching a team how to performance test, a reading group on cleaner code, a multi-sprint or multi-quarter initiative to mitigate tech debt etc.&lt;/p&gt;

&lt;p&gt;The tricky part is getting leaders to believe there is a problem and your time would be well spent by solving it for maximum impact. In the same way you have to forge relationships with seniors, regular check-ins with stakeholders up and across will become part of relationships to maintain such that you're able to get buy-in for your ideas. This larger the company, the longer it will take to build trust, and the more layered politics are.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create welcoming environments
&lt;/h2&gt;

&lt;p&gt;A team is just a group of people that trusts each other enough to work towards the same goal.&lt;/p&gt;

&lt;p&gt;Culture ends up being the things people feel able to say and do day-to-day at work; relational "vibes" are almost immediately recognizable within 2 weeks. Consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Whose opinions are valued when they speak?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do leaders ensure those who haven't said anything have their opinion invited?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How do leaders respond to genuine concerns, questions and improvements to the business?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Who ensures their work stays on track, and how do contributors respond to them?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Do people meet and talk outside of required meetings; are they excited to find ways to improve things?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are people of different levels open to sharing work-in-progress?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Are your leaders mature enough to handle inconvenient truths? What would that mean for how you can do your job?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Signs that people feel empowered and valued for going above and beyond, should read as whether this is a place you can thrive with your expertise.&lt;/p&gt;

&lt;p&gt;Creating a climate where people feel safe to surface glaring issues, troubleshoot and unblock each other, share solutions results in improved performance and delivery.&lt;/p&gt;

&lt;p&gt;What kinds of experiences of collaborating with others did you feel safe and accepted?&lt;/p&gt;

&lt;p&gt;It might be through pair-programming, group discussions, casual chit-chat on DM or getting to know someone's career path one-on-one.&lt;/p&gt;

&lt;p&gt;Without &lt;a href="https://www.infoq.com/articles/psychological-safety-tech-teams/" rel="noopener noreferrer"&gt;psychological safety&lt;/a&gt;, people become fearful of surfacing risks and incidents, get competitive for solo recognition, or become disengaged. Departures might be common. Remaining members might shoulder an unfair amount of work, leading to burnout.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elevate and Guide Others
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pick the most important fights and fires
&lt;/h3&gt;

&lt;p&gt;While working on a single project team, you might have an easy time agreeing on linting rules and naming patterns. If you hop between firefighting or unblocking different products and projects, you may slow a team down by asking them to change too many practices at once.&lt;/p&gt;

&lt;p&gt;No one is going to do everything the same way you would, and it's demoralizing for a more-or-less effective team to have someone point out every single flaw on their work. Your time and energy might be better spent solving ambiguous problems and turning stones others don't have the experience to look under.&lt;/p&gt;

&lt;p&gt;It takes courage and self-confidence to say what's achievable in a certain time, and whether that time from you or your team is worth it. It takes courage to also identify who might be underperforming and taking more time from you than they should.&lt;/p&gt;

&lt;p&gt;Recognize and reward initiative that is oriented to improving team performance and delivery. Acknowledging the effort people goes a long way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Protect your energy
&lt;/h2&gt;

&lt;p&gt;While managing a community and being a lead dev, I burned the candle at both ends by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;being too involved in everyday ops of all teams&lt;/li&gt;
&lt;li&gt;being too hung up on details of how things are done&lt;/li&gt;
&lt;li&gt;not pushing back on added priorities often enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spread myself too thin and was constantly underwater and completing the next most important task or making decisions to qualm whoever was complaining. Trying to make everyone happy isn't leadership, and people lose respect for you if you don't make hard decisions.&lt;/p&gt;

&lt;p&gt;If you are conflict-aversive or have immense people-pleasing syndrome, learning to say no to requests for help and have difficult conversations to advocate for your team (and yourself) will be challenging but necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The difference between now and after with that "Senior" title
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Before
&lt;/h3&gt;

&lt;p&gt;When you don't yet have the title people are more likely to treat you in the way that's "one of them" on the team. Your margin for error is relatively high. Reasonable workplaces will resource someone firefight if something goes south.&lt;/p&gt;

&lt;p&gt;In the absence of leadership some might even start turning to you as defacto leader for having all the know-how on the project. (Or they're just glad someone is doing the glue work.) Regardless of whether you're promoted, know what you did made you able to move a team and lead them towards delivery and ensure the success of a project. ✅&lt;/p&gt;

&lt;h3&gt;
  
  
  After
&lt;/h3&gt;

&lt;p&gt;After you land the title, all kinds of expectations and feedback come your way. Your margin for error lowers to a three-stamp punchcard, and recovery from them becomes crucial. (i.e. having a plan of action or finding workarounds, rolling out patches after discovering a solution you created is buggy throughout the software)&lt;/p&gt;

&lt;p&gt;If you were a very competent performer, a lot of what you did will still be your job. Code reviews, writing features, unblocking others, etc. But now you'll be expected to that on top of architectural meetings, learning initiatives, firefighting and validating technical decisions or solving problems with no clear prior path.&lt;/p&gt;

&lt;p&gt;Navigating ambiguity, full days of meetings to rectify and chunk down initiatives, reorder scope and providing the path forward for teams whether there is one, will become a regular part of the job.&lt;/p&gt;

&lt;p&gt;But if you've done this before, you can and will do it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making your way
&lt;/h2&gt;

&lt;p&gt;Figure out what kind of senior you want to be, gather impact notches on your resume, and start applying!&lt;/p&gt;

&lt;p&gt;The first 90 days and especially the first year, are a test of whether your idea of "senior" and a particular company's ideas of it aligns with your values and career goals.&lt;/p&gt;

&lt;p&gt;If it doesn't work out; don't take it as a personal failure.&lt;/p&gt;

&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;[ ] You get shit done. ✅&lt;/li&gt;
&lt;li&gt;[ ] You help others get shit done. ✅&lt;/li&gt;
&lt;li&gt;[ ] You communicate considerately. ✅&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's all you need to start interviewing in senior roles.&lt;/p&gt;

&lt;p&gt;Go forth!&lt;/p&gt;

&lt;h2&gt;
  
  
  Footnotes
&lt;/h2&gt;

&lt;p&gt;[^1] This is not to deride anyone who believes a title matters. Having a title is helpful for setting expectations around experience to external parties more than it matters internally. Tanya Reilly discusses this in &lt;a href="https://www.oreilly.com/library/view/the-staff-engineers/9781098118723/" rel="noopener noreferrer"&gt;"Staff Engineers' Path"&lt;/a&gt; –how Black women are more likely mis-recognized as janitors at workplaces. I am frequently mistaken for a student.&lt;/p&gt;

</description>
      <category>career</category>
      <category>senior</category>
      <category>staff</category>
      <category>wishiknew</category>
    </item>
    <item>
      <title>Closing, Cloning, or Disabling the Shadow DOM</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Wed, 01 Mar 2023 14:05:00 +0000</pubDate>
      <link>https://dev.to/jenc/closing-cloning-or-disabling-the-shadow-dom-2m00</link>
      <guid>https://dev.to/jenc/closing-cloning-or-disabling-the-shadow-dom-2m00</guid>
      <description>&lt;p&gt;If you have been furiously trying to figure out how to close, disable or extrapolate the contents of the shadow DOM for any following reasons... &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to prevent encapsulated styles from being manipulated by developers or content folks using your web component&lt;/li&gt;
&lt;li&gt;to secure sensitive code from being exposed to consumers&lt;/li&gt;
&lt;li&gt;in order to support light DOM because for whatever reason, your stakeholder or client believes that the shadow DOM must be eradicated and through any means, you must turn the web component into a "normal" component.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TL;DR: DON'T DO IT.&lt;/p&gt;

&lt;p&gt;For folks familiar with web components, this may seem like an arcane task that's doomed to fail, but I've been asked to do some version of the above twice in my short career. &lt;/p&gt;

&lt;p&gt;The title was just search bait. This is the real article 👇&lt;/p&gt;

&lt;h2&gt;
  
  
  5 Reasons Why You Should &lt;em&gt;NOT&lt;/em&gt; Close, Clone, Or Disable the Shadow DOM
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1. Browser APIs prevent disabling attachment of a shadow root
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry/define#creating_an_element_which_disables_the_ability_to_attach_a_shadow_root" rel="noopener noreferrer"&gt;&lt;code&gt;CustomElementRegistry.define()&lt;/code&gt; specification&lt;/a&gt; specifically prevents the use of &lt;code&gt;static disabledFeature&lt;/code&gt; from being applied to disable the attachment of shadow root on a web component.&lt;/p&gt;

&lt;p&gt;Your console will return a &lt;code&gt;DOMException NotSupportedError&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Browser APIs don't support cloning
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4v8gk402khqgrlj8f4df.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4v8gk402khqgrlj8f4df.png" alt="One cannot simply walk into Mordor Game of Thrones meme replaced with " width="651" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The instant developer sensibility when I heard the client desire to convert shadow DOM children to light DOM ones: well, you gotta parse and transform DOM slotted nodes into generic HTML elements, then move them into the light DOM, right? &lt;/p&gt;

&lt;p&gt;With that we reach for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode" rel="noopener noreferrer"&gt;&lt;code&gt;Element.cloneNode()&lt;/code&gt;&lt;/a&gt;... and we'll find the error: &lt;br&gt;
&lt;code&gt;NotSupported ... "ShadowRoot nodes are not clonable."&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here are the exact &lt;a href="https://chromium.googlesource.com/external/w3c/web-platform-tests/+/master/shadow-dom/Node-prototype-cloneNode.html" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; and and &lt;a href="https://chromium.googlesource.com/chromium/src/+/c47ae70a879d7247c56a842716f4ab67ac25a71d/third_party/WebKit/Source/core/dom/shadow/ShadowRoot.cpp#107" rel="noopener noreferrer"&gt;Chromium&lt;/a&gt; tests preventing that.&lt;/p&gt;

&lt;p&gt;At this point, I should have probably stopped, but I didn't.&lt;/p&gt;

&lt;p&gt;Oh no, being unable to accept failure when presented with heroic feats of front end... I just have to see where this goes.&lt;/p&gt;

&lt;p&gt;Even though I couldn't clone the shadow root node, I was able to grab its children, parse and transform them into HTML elements and reattach them to their rightful parent. &lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-h1bj7f?file=src/components/cloned-shadow-dom-button/cloned-shadow-button.ts&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Note: I used Fast Design to try out the above while doing my research.&lt;/p&gt;

&lt;p&gt;Easy win, right? &lt;br&gt;
&lt;em&gt;IT WORKED IN MY DOM.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhv06bp8v4xmcyresyd0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhv06bp8v4xmcyresyd0.gif" alt="flash of light DOM web component" width="480" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But no. Utter failure. I get a "flash of light DOM component". As the shadow DOM remains open, styles continue to be scoped towards shadow DOM elements, looking to scope those styles towards flattened, projected content. &lt;/p&gt;

&lt;p&gt;Cloning and moving shadow DOM children nodes, transforming slots to HTML elements and attaching them to their "light DOM" parent element also easily re-trigger lifecycle events via changing the position of items within the DOM. And I &lt;em&gt;had&lt;/em&gt; to do all the above within the class constructor because that's where a shadow root is attached. &lt;/p&gt;

&lt;p&gt;I had thrown in a &lt;code&gt;setTimeout()&lt;/code&gt; to prevent race conditions between the constructor operations and the component registration in the DOM.&lt;/p&gt;

&lt;p&gt;This led to multiple re-renders and attachments (go figure, moving the DOM order of children in a component is going to trigger &lt;code&gt;connectedCallback()&lt;/code&gt; again and again!)&lt;/p&gt;

&lt;p&gt;Well all the above required keeping the shadow DOM open, cloning the shadow tree, parsing it, then to close it all within runtime. As long as the shadow DOM remained open, styles continue to be scoped towards &lt;/p&gt;

&lt;p&gt;This leads to my next epiphany... 👇 &lt;/p&gt;
&lt;h2&gt;
  
  
  3. Opening or Closing Shadow DOM on Runtime: Impossible
&lt;/h2&gt;

&lt;p&gt;In my previous example, I kept the shadow DOM open, cloned and transformed its children to light DOM nodes, and tried to close it as a final act to disable the shadow DOM so that my children could render with their new light DOM styles. &lt;/p&gt;

&lt;p&gt;I made another attempt at using the shiny-new, experimental declarative &lt;code&gt;shadowroot&lt;/code&gt; API that's only available in Chrome currently on a template. &lt;/p&gt;

&lt;p&gt;Both were resounding failures. Why?&lt;/p&gt;
&lt;h3&gt;
  
  
  The shadow mode is set once on class instantiation
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvzkjf4h7rnalmgf2yzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgvzkjf4h7rnalmgf2yzm.png" alt="Figure 4.1. The start of a Web Component’s lifecycle: constructor first, and then connectedCallback after adding to the DOM, from " width="653" height="906"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If open, the shadow DOM can't be closed during runtime as the constructor that instantiates the attachment of a shadow root determines the &lt;code&gt;open&lt;/code&gt; or &lt;code&gt;close&lt;/code&gt; mode it is attached to the web component on instantiation.&lt;/p&gt;

&lt;p&gt;You're going to get this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught DOMException: Failed to execute 'createShadowRoot'
on 'Element': Shadow root cannot be created on a host which already hosts a shadow tree.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once a shadow mode is attached and connected, it &lt;a href="https://stackoverflow.com/questions/20090059/how-to-remove-a-shadow-root-from-an-html-element-adorned-with-a-shadow-dom-from" rel="noopener noreferrer"&gt;can't&lt;/a&gt; be closed or deleted, but you can attach new ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. A Closed Shadow DOM doesn't secure sensitive code
&lt;/h2&gt;

&lt;p&gt;If you want to hide your styles and prevent them from being altered by whoever's using the components, fiiinee. This is definitely a way to prevent folks from adding extraneous classes to the component in the light DOM.&lt;/p&gt;

&lt;p&gt;Writing on shadow DOM V1, Eric Bidelman warns of how the functional and style encapsulation of shadow DOM can be &lt;a href="https://web.dev/shadowdom-v1/#creating-closed-shadow-roots-should-avoid" rel="noopener noreferrer"&gt;misunderstood as a security measure&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Closed shadow roots are not very useful. Some developers will see closed mode as an artificial security feature. But let's be clear, it's not... Closed mode simply prevents outside JS from drilling into an element's internal DOM.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A black hatter could still inject malicious code into the component constructor.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Ditch Shadow DOM; Lose Thy Scoped Styles
&lt;/h2&gt;

&lt;p&gt;As a result of disabling the shadow DOM or converting its children to light DOM elements, you lose the very benefit it offers in web components: scoped styles. &lt;/p&gt;

&lt;p&gt;Hypothetically you &lt;em&gt;could&lt;/em&gt; use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/adoptedStyleSheets" rel="noopener noreferrer"&gt;&lt;code&gt;adoptedStylesheets()&lt;/code&gt;&lt;/a&gt; to replace a pre-existing scoped styles with your new light DOM styles... but you'll have to figure out how to style your DOM to avoid conflicting styles in global CSS namespace. That's a lot of work to do for something you already did once.&lt;/p&gt;

&lt;p&gt;Now you need 2 sets of styles to alternate between! One that supports scoped styles, and the other that supports light DOM styles! Why would you do double the work?!&lt;/p&gt;

&lt;h3&gt;
  
  
  Disable shadow DOM: lose slots, styles, all good things about web components
&lt;/h3&gt;

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

&lt;p&gt;Finally, my peer &lt;a class="mentioned-user" href="https://dev.to/tebin"&gt;@tebin&lt;/a&gt; tried disabling the shadow DOM by passing &lt;code&gt;shadowOptions: null&lt;/code&gt; into the &lt;code&gt;define()&lt;/code&gt; (custom element registry method provided by FAST). By parsing and reattaching slot-referencing children in a light DOM parent, he was trying to see if slots would continued to be supported. But no! In this case we lost all the style encapsulation and previously slotted content would render out of order.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/vitejs-vite-h1bj7f?file=src/components/light-dom-slot/light-dom-slot.ts&amp;amp;view=editor" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Most components in web applications and on websites, are based in Javascript. While React and Angular use a series of functions to abstract the creation and rendering of components in client-side applications, web components leverage browser-based APIs and HTML standards. &lt;/p&gt;

&lt;p&gt;Would you attempt to re-engineer how React handles children as "slots", or change the way &lt;code&gt;React.createElement()&lt;/code&gt; works ...in the same way I tried to close the shadow DOM or make it's children work in the light DOM? (I hope not)&lt;/p&gt;

&lt;p&gt;Customizing a technology to do the opposite of how it works-on command-usually ends in increasing friction with the mechanisms that make it behave the way it does. &lt;/p&gt;

&lt;p&gt;If web components was already made as a choice for "build once, use everywhere" then your next spikes might be: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What can I do within the environment(s) I'm integrating this in to overcome perceived barriers of shadow DOM? &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What can I do with the existing features and APIs of the framework I'm using? (with web components, it may be the framework you're using. If you're working in vanilla JS, it's most definitely browser APIs.)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me know what you've tried!&lt;/p&gt;

&lt;p&gt;Thanks to Tebin Raouf &lt;a class="mentioned-user" href="https://dev.to/tebin"&gt;@tebin&lt;/a&gt;, Simon Macdonald &lt;a class="mentioned-user" href="https://dev.to/macdonst"&gt;@macdonst&lt;/a&gt; for programmatic support and guidance along the way!&lt;/p&gt;

&lt;h2&gt;
  
  
  Related Reading
&lt;/h2&gt;

&lt;p&gt;[1] The question of removing the shadow DOM or creating the topic has been reported at the &lt;code&gt;webcomponents/polyfills&lt;/code&gt; repository under issue &lt;a href="https://github.com/webcomponents/polyfills/issues/82" rel="noopener noreferrer"&gt;#82&lt;/a&gt; , and &lt;code&gt;svelte/sveltjs&lt;/code&gt; issue &lt;a href="https://github.com/sveltejs/svelte/issues/1748" rel="noopener noreferrer"&gt;#1748&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;[2] E. Bidelman. &lt;a href="https://web.dev/shadowdom-v1/" rel="noopener noreferrer"&gt;"Shadow DOM v1 - Self-Contained Web Components"&lt;/a&gt;, Web.dev by Chrome Devrel. Jun 27, 2020&lt;/p&gt;

&lt;p&gt;[3] A. Sundara, &lt;a href="https://blog.ankursundara.com/shadow-dom/" rel="noopener noreferrer"&gt;"The Closed Shadow DOM"&lt;/a&gt;, May 12, 2022&lt;/p&gt;

</description>
      <category>webcomponents</category>
      <category>healthydebate</category>
      <category>fastdesign</category>
    </item>
    <item>
      <title>Maintaining a Healthy-Enough Mindset through Uncertainty</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Mon, 05 Dec 2022 17:41:22 +0000</pubDate>
      <link>https://dev.to/jenc/maintaining-a-healthy-enough-mindset-through-uncertainty-3j8e</link>
      <guid>https://dev.to/jenc/maintaining-a-healthy-enough-mindset-through-uncertainty-3j8e</guid>
      <description>&lt;p&gt;Climate change, big tech layoffs, inflation, AI replacing work... Things have seemed grim for working in tech.&lt;/p&gt;

&lt;p&gt;But this can't be farther from the truth.&lt;/p&gt;

&lt;p&gt;I don't know of a job where I can work from anywhere, find help I need at any hour, participate in spirited debates, and wear t-shirts to work. The demand for talent and experience hasn't reduced even if large companies have been doing layoffs. Canadian talent continues to brain drain to the US.&lt;/p&gt;

&lt;p&gt;This post isn't going to show you how to keep or find a job, but how you might reframe your thinking to manage anxiety around changing conditions at work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Competency vs. Likability
&lt;/h2&gt;

&lt;p&gt;The cultivation of  competence at work (or on paper) is a strange performance. Women, non-binary, BIPOC and trans folks have the added challenge of nailing this the first 20 minutes of a job interview, and then nurturing it in the first months at a new role -or that perception is stuck to you til you switch jobs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffc3y2rtobjjhwej4kp2x.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffc3y2rtobjjhwej4kp2x.gif" alt="Competency vs Likability Matrix from Harvard Business Review" width="390" height="194"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to show the right amount of ambition and political savvy–you can't be too thirsty, you have to be knowledgeable without being intimidating, friendly while clear with boundaries, assertive about expectations without appearing aggressive. &lt;/p&gt;

&lt;p&gt;I don't have answers for this and I'm still figuring it out. &lt;br&gt;
What I do know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When you're new, asking as many questions about how things are done or restating that you're new helps people guide you.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you've been overly approval-seeking then you're giving whoever you work for all the power to determine your worth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you act vulnerable or  transparent, watch whether people use it as ammo against your performance-you may be working somewhere that punishes honesty and shoots messengers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you are reading this as one recently deemed "incompetent" or just-fired, take the time to process it. It happens more often than people talk about, and there are likely more factors at play that led up to that move, than how you're thinking of yourself now.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create Virtuous Cycles
&lt;/h2&gt;

&lt;p&gt;One thing I've learned about improving relationships at work is to recognize the roles others had in helping you get your work done. For example, your manager does long meetings with clients so you can spend more time coding. Clue into what others are working on day-to-day, and share openly with them what you're doing. &lt;/p&gt;

&lt;p&gt;Appreciate the hidden labor that teammates shoulder to fill in gaps due to understaffing or someone else's incompetence. Is there someone who's setting up meetings and writing incredible doc? They're doing extra to make everyone's lives easier... and sometimes, there is simply no agenda other than to reach the goals your company paid you both to do more easily.&lt;/p&gt;

&lt;p&gt;Focus on &lt;a href="https://addyosmani.com/blog/high-leverage-activites/" rel="noopener noreferrer"&gt;high leverage activities&lt;/a&gt; that are easy for you to do but helps others move ahead and grow disproportionately. When you notice others doing this, go to their manager to commend them.&lt;/p&gt;

&lt;p&gt;Help others help you, and help others help others. ✌️✌️✌️&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qiu1ten62cqiaza1srj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2qiu1ten62cqiaza1srj.gif" alt="Help me, help you, says Tom Cruise in Jerry McGuire" width="540" height="310"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Find Purpose
&lt;/h2&gt;

&lt;p&gt;I heard most people spend two thirds of their adult life doing this thing called "work". &lt;/p&gt;

&lt;p&gt;While not everyone needs purpose to make a living, figuring out the following helps with career planning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What do you enjoy about being a developer?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Which aspect of development do you most enjoy?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What are your strengths vs what you find easy to do and learn?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where did you envision yourself when you started your programming journey, and what would you tell yourself now?&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Write these down and begin making realistic micro-goals to get there. &lt;/p&gt;

&lt;p&gt;When you know where you want to go next, you begin choosing more appropriate opportunities. &lt;/p&gt;

&lt;p&gt;You begin defining success for yourself.&lt;/p&gt;

&lt;p&gt;More about &lt;a href="https://www.ted.com/talks/simon_sinek_how_great_leaders_inspire_action" rel="noopener noreferrer"&gt;finding purpose&lt;/a&gt; by Simon Sinek.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work on your Internal Locus of Control
&lt;/h2&gt;

&lt;p&gt;You can't control the weather or the interest rates, but you can control how you respond to change, and how you talk about yourself.&lt;/p&gt;

&lt;p&gt;Counteract chastising yourself for mistakes with thinking ahead on how you can prevent them going forward. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragmaticthinking.com%2Fwp-content%2Fuploads%2F2021%2F08%2FLocus-of-Control.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fpragmaticthinking.com%2Fwp-content%2Fuploads%2F2021%2F08%2FLocus-of-Control.jpg" alt="Locus of Control diagram" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Recognize the Impact of Scarcity and Abundance Mentality
&lt;/h2&gt;

&lt;p&gt;Have you worked for people who operated out of fear of repercussions? This usually happens in workplaces with authoritarian leadership styles. Fear of failure ends up stifling any experimentation or innovation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fchopra.brightspotcdn.com%2Fdims4%2Fdefault%2F0e9438f%2F2147483647%2Fstrip%2Ftrue%2Fcrop%2F696x628%2B0%2B0%2Fresize%2F840x758%21%2Fquality%2F90%2F%3Furl%3Dhttp%253A%252F%252Fchopra-brightspot.s3.amazonaws.com%252Fcd%252F58%252F0b7656ecb8996070f8109a953cd7%252Funtitled-1-0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fchopra.brightspotcdn.com%2Fdims4%2Fdefault%2F0e9438f%2F2147483647%2Fstrip%2Ftrue%2Fcrop%2F696x628%2B0%2B0%2Fresize%2F840x758%21%2Fquality%2F90%2F%3Furl%3Dhttp%253A%252F%252Fchopra-brightspot.s3.amazonaws.com%252Fcd%252F58%252F0b7656ecb8996070f8109a953cd7%252Funtitled-1-0.jpg" alt="Abundance mentality vs. scarcity mentality" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even without blatant abusive moves, it can manifest in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;totally quiet meetings where no one talks and everyone is disengaged&lt;/li&gt;
&lt;li&gt;refusal to take responsibility or make decisions&lt;/li&gt;
&lt;li&gt;deferring the completion of planned tasks even when help is offered&lt;/li&gt;
&lt;li&gt;my-way-or-the-highway approaches to disagreement&lt;/li&gt;
&lt;li&gt;micro-managing behaviours, including the nitpicking of spelling, grammar or wording&lt;/li&gt;
&lt;li&gt;requests for many revisions that don't lead to any solid business outcomes&lt;/li&gt;
&lt;li&gt;sudden changes in requirements as you are working on something&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a few experiences, you'll begin to notice that the same work could be done  with personalities that possess abundance mindset--people who are open to learning, being wrong and trying again. It's also not your job to help them through this or be the emotional urinal for their concerns. If you're a consultant however, maybe you'll win points by just continuing to bill :D &lt;/p&gt;

&lt;p&gt;It takes immense trust and confidence to admit fault, solution and mitigate errors effectively as a team. I've only learned the importance of this by working with people with more experience who had a steady and focused manner of dealing with problems on the eve of a delivery. So it's a net win for productivity and time-to-hotfix if you have a culture of openness.&lt;/p&gt;

&lt;p&gt;Some of these qualities are expanded on in the &lt;a href="https://cloud.google.com/architecture/devops/devops-culture-westrum-organizational-culture" rel="noopener noreferrer"&gt;Westrum Organizational Culture Model&lt;/a&gt; by devops research at Google. &lt;/p&gt;

&lt;p&gt;Reduce contact with those who fan the flames of scarcity mentality, or try to find a way to work &lt;em&gt;around&lt;/em&gt; them. Whether intentionally or not, their maneuvers slowly break down the people who work with them.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Anecdote time
&lt;/h2&gt;

&lt;p&gt;Earlier in my career I went through some workplaces that expected immediate results, where unforgiving attitudes and long hours were the norm. I became extremely resourceful at getting things done, but internalized the belief anything I did wasn't "good enough yet". By my fifth job, I was shit-talking work I did that was filling in for a role above my level, while presenting to higher-ups.&lt;/p&gt;

&lt;p&gt;Thankfully, some caring senior leaders took me aside and made me aware of the unhealthy self-judgment I carried with me from previous workplaces. I was hard on myself, and this sent a message that I was hard on others. &lt;/p&gt;

&lt;p&gt;By not objectively taking stock of my own achievements and constantly comparing myself to others,  I didn't have a clear sense of what the reality of demand for my skillset was on the market.&lt;/p&gt;




&lt;p&gt;What I wrote here is pretty corny and probably took a decade of reflection across 2 industries, and I-don't-know-how-many-hours of therapy. &lt;/p&gt;

&lt;p&gt;What have you recognized as fears about your own employability or longevity at work?&lt;/p&gt;

&lt;p&gt;How did you cope with it?&lt;/p&gt;

&lt;p&gt;What are ways you've helped others through a difficult period in their career?&lt;/p&gt;

</description>
      <category>mentalhealth</category>
      <category>impostersyndrome</category>
      <category>career</category>
      <category>growth</category>
    </item>
    <item>
      <title>Build Time, Runtime, Execution time... What "time" is it in Javascript?</title>
      <dc:creator>Jen Chan</dc:creator>
      <pubDate>Wed, 23 Nov 2022 15:03:20 +0000</pubDate>
      <link>https://dev.to/jenc/build-time-runtime-execution-time-what-time-is-it-in-javascript-42ea</link>
      <guid>https://dev.to/jenc/build-time-runtime-execution-time-what-time-is-it-in-javascript-42ea</guid>
      <description>&lt;p&gt;Last month, I was struggling to give good answers to questions like "Why can't I just plug a &lt;code&gt;.scss&lt;/code&gt; file into a link element's &lt;code&gt;href&lt;/code&gt; and have that work in development?", or "Why can't I reference a &lt;code&gt;.css&lt;/code&gt; file from a neighbouring project's &lt;code&gt;node_modules&lt;/code&gt; within a static file?"&lt;/p&gt;

&lt;p&gt;A crucial reason for this is due to the way JS frameworks rely on multiple transpiling and bundling processes. &lt;/p&gt;

&lt;p&gt;The rapid visual feedback of hot module reloading (HMR) in local development environments can make us forget that the outgoing application code doesn't treat files the same way!&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime vs. Build Time
&lt;/h2&gt;

&lt;p&gt;We can't discuss what a "build" means in a Javascript application without mentioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runtime&lt;/li&gt;
&lt;li&gt;build time&lt;/li&gt;
&lt;li&gt;parsing and execution time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Note: I left out compile time since JS is an &lt;a href="https://www.geeksforgeeks.org/difference-between-compiled-and-interpreted-language/" rel="noopener noreferrer"&gt;interpreted language&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime
&lt;/h2&gt;

&lt;p&gt;Runtime happens when the software is executing. This can be when an application makes requests and the server responds–maybe after a server-side computed process.&lt;/p&gt;

&lt;p&gt;Runtime also refers to the actual tool running the code: like &lt;a href="https://nodejs.org/en/about/" rel="noopener noreferrer"&gt;Node&lt;/a&gt;, or &lt;a href="https://deno.land/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt;.*&lt;/p&gt;

&lt;p&gt;The idea is further confused by the fact we use &lt;code&gt;node&lt;/code&gt; or &lt;code&gt;npm&lt;/code&gt; in our commands to &lt;strong&gt;run&lt;/strong&gt; the runtime in local dev environments. 😂&lt;/p&gt;

&lt;p&gt;TLDR: &lt;code&gt;npm&lt;/code&gt; and &lt;code&gt;yarn&lt;/code&gt; are package managers. &lt;/p&gt;

&lt;p&gt;Node and Deno are runtimes that create an environment for the code you write to be executed, built, even deployed. &lt;/p&gt;

&lt;p&gt;As you're developing, runtimes allow you to access backends, enter environment variables--all in the same place without the need for a browser.&lt;/p&gt;

&lt;p&gt;*If you wanted to split hairs, the browser is arguably a runtime environment for Javascript files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Time
&lt;/h2&gt;

&lt;p&gt;Build time consists of the following processes:&lt;/p&gt;

&lt;h3&gt;
  
  
  Compiling/Transpiling
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fim5kusvdr2xknpnu9256.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fim5kusvdr2xknpnu9256.png" alt="Compiling and build processes in React" width="800" height="566"&gt;&lt;/a&gt;&lt;br&gt;
&lt;sup&gt;Jonas Bondi. &lt;a href="https://medium.jonasbandi.net/angular-vs-react-compilers-45b279a8f571" rel="noopener noreferrer"&gt;Angular vs. React: Compilers&lt;/a&gt;, Medium, 2017.&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;When you write modern Javascript apps, you usually need to outfit your project with a transpiler like Babel or Typescript to compile the Javascript to ES5 or CommonJS, a form of Javascript that Node was initially introduced in, and all browsers could parse. &lt;/p&gt;

&lt;p&gt;In the case of CSS preprocessors like Sass, it is compiling to CSS. With PostCSS, unique classes might be generated and treeshaken.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minifying
&lt;/h3&gt;

&lt;p&gt;Merging all code into a single file and removing all excessive spacing such that it is compact, and bundling and runs faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Codesplitting
&lt;/h3&gt;

&lt;p&gt;Chunking and separating code to relevant &lt;code&gt;.js&lt;/code&gt; files -- such that you're able to import just the files pertinent to a button component, instead of a whole library.&lt;/p&gt;

&lt;p&gt;When you run &lt;code&gt;npm run build&lt;/code&gt;, all processes mentioned in above are started to prepare a production ready bundle of Javascript, CSS and HTML (often with source maps) that will be ready for production deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Development mode
&lt;/h2&gt;

&lt;p&gt;During development mode, the build tool Webpack's &lt;a href="https://webpack.js.org/guides/hot-module-replacement/" rel="noopener noreferrer"&gt;Hot Module Replacement (HMR)&lt;/a&gt; feature allows applications to conveniently rebuild at runtime.&lt;/p&gt;

&lt;p&gt;In most cases a local cache folder of application code is built, with subsequent changes constantly rebuilt to give developers fast-feedback of end-users' experience as you're developing... &lt;em&gt;but&lt;/em&gt; all those fresh buildz don't appear in your output (&lt;code&gt;dist/&lt;/code&gt;) folder.&lt;/p&gt;

&lt;p&gt;Today there are so many fancy features that optimize the dev experience of being able to preview the code you're writing in JS frameworks (&lt;em&gt;cough&lt;/em&gt; &lt;a href="https://angular.io/guide/aot-compiler" rel="noopener noreferrer"&gt;Ahead-Of-Time compiling&lt;/a&gt; in Angular, &lt;a href="https://www.gatsbyjs.com/docs/how-to/performance/improving-build-performance/" rel="noopener noreferrer"&gt;incremental builds&lt;/a&gt; in Gatsby and for Next, &lt;a href="https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration" rel="noopener noreferrer"&gt;Incremental Static Regeneration&lt;/a&gt; uses such caches both in dev and on the server to decrease the rebuild times for its statically generated views.) &lt;/p&gt;

&lt;p&gt;With the rise of a newer generation of build tools and bundlers written in Rust and Go, build times are drastically lowering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution Time
&lt;/h2&gt;

&lt;p&gt;Execution happens in the browser. In single page applications, the minified files returned from the server go through what happens &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Getting_started_with_the_web/How_the_Web_works#so_what_happens_exactly" rel="noopener noreferrer"&gt;after you type the URL into the browser&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lrujegeawj6bikps9p7.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0lrujegeawj6bikps9p7.webp" alt="Illustration of how the Javascript v8 engine works by Addy Osmani" width="800" height="577"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++. It is used in Chrome and in Node.js..." &lt;a href="https://v8.dev/" rel="noopener noreferrer"&gt;v8.dev&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h2z6iz3vvklotkp287f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6h2z6iz3vvklotkp287f.jpg" alt="Illustration Figure 2.1. The lifecycle of a client-side web application starts with the user specifying a website address (or clicking a link) and ends when the user leaves the web page. It’s composed of two steps: page building and event handling. from " width="590" height="580"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Fig 2.1 The lifecycle of a client-side web application starts with the user specifying a website address (or clicking a link) and ends when the user leaves the web page. It’s composed of two steps: page building and event handling.&lt;br&gt;
&lt;a href="https://www.manning.com/books/secrets-of-the-javascript-ninja-second-edition" rel="noopener noreferrer"&gt;"Secrets of the Javascript Ninja, Second Edition"&lt;/a&gt; by John Resig, Bear Bibault, Josip Maras, August 2016.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the web 1.0 times, a DOM tree would be created by parsing of html files. These days, JS functions (like &lt;code&gt;React.createElement()&lt;/code&gt;) create and append those nodes. &lt;/p&gt;

&lt;p&gt;On &lt;a href="https://developer.chrome.com/blog/inside-browser-part2/#extra-step-initial-load-complete" rel="noopener noreferrer"&gt;initial load&lt;/a&gt;, JS is read (parsed) top to bottom once, and the relevant execution contexts are set for every single function invocation in a first-on-first-off manner. As the session continues, functions are invoked asynchronously in a last-in-first-out manner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue9gb55ew4avu36fnkvd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fue9gb55ew4avu36fnkvd.webp" alt="Illustration of execution context stack" width="800" height="142"&gt;&lt;/a&gt;&lt;br&gt;
Sukhjinder Arora,&lt;a href="https://blog.bitsrc.io/understanding-execution-context-and-execution-stack-in-javascript-1c9ea8642dd0" rel="noopener noreferrer"&gt;"Understanding Execution Context and Execution Stack in Javascript"&lt;/a&gt;, Medium, 2018.&lt;/p&gt;

&lt;p&gt;Aside from the load time, scripting time constitutes the registering of event listeners and dynamic content during the session. &lt;/p&gt;

&lt;p&gt;A lot of &lt;a href="https://kbpsystem777.github.io/You-Dont-Know-JS/" rel="noopener noreferrer"&gt;foundational&lt;/a&gt; &lt;a href="https://eloquentjavascript.net/" rel="noopener noreferrer"&gt;resources&lt;/a&gt; cover Javascript execution, but rarely do we connect all types of "time" to see where they fit together!&lt;/p&gt;




&lt;p&gt;Which build tools are you excited for in 2023? &lt;/p&gt;

&lt;p&gt;What are concepts that you found hard to place in your programming career? &lt;/p&gt;

&lt;p&gt;Let me know in the comments.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a class="mentioned-user" href="https://dev.to/naismith"&gt;@naismith&lt;/a&gt; &lt;a href="https://shreyadahal.com" rel="noopener noreferrer"&gt;Shreya Dahal&lt;/a&gt;, &lt;a href="https://twitter.com/CheapSteak" rel="noopener noreferrer"&gt;Chang&lt;/a&gt;, Jimmy Jansen, Matt Mackay for helping me clarify my mental models!&lt;/p&gt;




&lt;h2&gt;
  
  
  Related Reading
&lt;/h2&gt;

&lt;p&gt;Dan Abramov. &lt;a href="https://overreacted.io/how-does-the-development-mode-work/" rel="noopener noreferrer"&gt;"How does the development mode work?"&lt;/a&gt;, &lt;em&gt;overreacted&lt;/em&gt;. August 4, 2019.&lt;/p&gt;

&lt;p&gt;Hugh Haworth. &lt;a href="https://css-tricks.com/comparing-the-new-generation-of-build-tools/" rel="noopener noreferrer"&gt;"Comparing the New Generation of Build Tools"&lt;/a&gt;, &lt;em&gt;css-tricks&lt;/em&gt;, Apr 8, 2021 (Updated on Jan 5, 2022)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://2021.stateofjs.com/en-US/libraries/build-tools/" rel="noopener noreferrer"&gt;"State of JS 2021: Build Tools"&lt;/a&gt;, State of JS. 2022.&lt;/p&gt;

&lt;p&gt;Mariko Kosaka. &lt;a href="https://developer.chrome.com/blog/inside-browser-part3/" rel="noopener noreferrer"&gt;"Inside look at modern web browser (part 3)"&lt;/a&gt; Chrome Developers, August 18, 2020.&lt;/p&gt;

&lt;p&gt;Addy Osmani. &lt;a href="https://medium.com/reloading/javascript-start-up-performance-69200f43b201" rel="noopener noreferrer"&gt;"Javascript Start-up Performance"&lt;/a&gt;, Feb 9, 2017.&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>javascript</category>
      <category>intermediate</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
