<?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: Daniel Macák</title>
    <description>The latest articles on DEV Community by Daniel Macák (@daelmaak).</description>
    <link>https://dev.to/daelmaak</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%2F919639%2F7af0b022-6ae7-4b52-8f12-924f95f1e072.jpeg</url>
      <title>DEV Community: Daniel Macák</title>
      <link>https://dev.to/daelmaak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/daelmaak"/>
    <language>en</language>
    <item>
      <title>How to test TanStack Router</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Wed, 16 Apr 2025 08:32:08 +0000</pubDate>
      <link>https://dev.to/daelmaak/how-to-test-tanstack-router-4f51</link>
      <guid>https://dev.to/daelmaak/how-to-test-tanstack-router-4f51</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclaimer: I talk primarily about the code-based routing using React.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I recently rewrote an app to use a TanStack Router and naturally wondered how to test our routing. &lt;/p&gt;

&lt;h2&gt;
  
  
  We are on our own here 😮
&lt;/h2&gt;

&lt;p&gt;To my surprise, the &lt;a href="https://tanstack.com/router/latest/docs/framework/react/overview" rel="noopener noreferrer"&gt;official docs&lt;/a&gt; don't mention testing at all. I found some &lt;a href="https://github.com/TanStack/router/discussions/655" rel="noopener noreferrer"&gt;mentions on GH&lt;/a&gt; but these are just indirect hints. That might be because they think there is nothing special about the test setup, but it still leaves &lt;a href="https://www.reddit.com/r/reactjs/comments/1bpfze1/how_can_i_test_tanstackrouter/" rel="noopener noreferrer"&gt;people confused&lt;/a&gt; about best practices or just a basic setup.&lt;/p&gt;

&lt;p&gt;So I took a quick stab at this challenge and figured out a couple things that will hopefully make it work for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complete example
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;example.test.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMemoryHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;RouterProvider&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;@tanstack/react-router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&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;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;act&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;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;renders app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&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;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello Router&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeTruthy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setUp&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;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// Very important to prevent test slowdown&lt;/span&gt;
    &lt;span class="na"&gt;defaultPendingMinMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;routeTree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChildren&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;TEST_INDEX_ROUTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;realRoutes&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouterProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;
  &lt;span class="c1"&gt;// Either you can make this part of setup, or call it in each test manually&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;act&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;search&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_uuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;account_uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/account&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;router&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;// Optional, if you want to prevent real components from loading initially&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TEST_INDEX_ROUTE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Dummy&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;,
&lt;/span&gt;  &lt;span class="na"&gt;getParentRoute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;realRootRoute&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With a code snippet out of the way, let's talk details and some important principles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep it real
&lt;/h2&gt;

&lt;p&gt;You might have noticed that I am using real routes for both the root route and the routes under test. This is exactly what you want in order to keep your tests as close to the production environment as possible.&lt;/p&gt;

&lt;p&gt;Ideally, I wouldn't need the &lt;code&gt;TEST_INDEX_ROUTE&lt;/code&gt; either and I'd just use the production routes, but I didn't want to navigate to a real "/" route and initialize components I didn't want to test. That's why I am using this dummy as a starting point.&lt;/p&gt;

&lt;p&gt;In doing so, my testing router now differs from the production one. So I need to hint this fact to my router provider as it would expect the production router type by default and TS would produce a type mismatch error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;defaultPendingMinMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;routeTree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChildren&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;TEST_INDEX_ROUTE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;realRoutes&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// Let TS know we are using a different router&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;RouterProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The alternative way is to use the real routes but set the initial route to something else than &lt;code&gt;/&lt;/code&gt; using custom memory history:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;memoryHistory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMemoryHistory&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;initialEntries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/account?account_uuid=account-uuid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;initialIndex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;defaultPendingMinMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;memoryHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;routeTree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rootRoute&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addChildren&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;refundRoutes&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;Not bad, but I don't like that the initial URL has to be specified as a URI string without any type guarantees. On the other hand, calling &lt;code&gt;navigate&lt;/code&gt; explicitly gives you full control and type safety. Just don't forget to wrap it in &lt;code&gt;act&lt;/code&gt; since navigation often changes React state and you'd get warnings and perhaps wrong state if you didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Beware the pending state
&lt;/h2&gt;

&lt;p&gt;Maybe you wondered what this setting is about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createRouter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// Very important to prevent test slowdown&lt;/span&gt;
    &lt;span class="na"&gt;defaultPendingMinMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default value is 500ms and it's the time during which the route is in a pending (not displayed) state. I gathered that it should make people navigating between pages aware that the page changed. This to me sounds like a hard to justify slow down and I always recommend setting it to 0 in production code.&lt;/p&gt;

&lt;p&gt;But it's easy to forget that you need the same setting in tests as well, otherwise it can prolong each of your test cases by 500ms 🙈.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;I found that tests involving Tanstack Router and full route rendering tend to be pretty slow - couple 100s ms. I'd recommend writing such tests only for the complex parts of your navigation and perhaps use shallow rendering to improve performance if absolutely necessary.&lt;/p&gt;

&lt;p&gt;The good news is that lazy routes are respected in that they are not loaded until used. That isn't really surprising since the lazy loading, using the dynamic &lt;code&gt;import()&lt;/code&gt; is pure JS and nothing specific to Tanstack Router.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;As you see, there is not much test specific set up needed, and it's indeed the best strategy to change as little as possible for the sake of tests.&lt;/p&gt;

&lt;p&gt;Let me know if you found a better way to set the tests up with Tanstack Router, especially regarding the performance. Cheers.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Astro + Vercel Serverless API</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Tue, 03 Sep 2024 07:48:47 +0000</pubDate>
      <link>https://dev.to/daelmaak/astro-vercel-serverless-api-12l8</link>
      <guid>https://dev.to/daelmaak/astro-vercel-serverless-api-12l8</guid>
      <description>&lt;p&gt;I recently decided to create a portfolio since I never had one. It would be a static content mostly with a few dynamic islands here and there. Namely I'd like to dynamically fetch my Dev.to blogposts, and I don't care how I do it as long as I stay on the cheap side.&lt;/p&gt;

&lt;p&gt;Since I mentioned islands, you probably guessed I picked Astro. And since I like using Vercel, I thought I could use an &lt;a href="https://vercel.com/docs/frameworks/astro#edge-functions" rel="noopener noreferrer"&gt;Edge Function&lt;/a&gt; to make &lt;a href="https://dev.to/stephengade/how-to-fetch-your-devto-articles-for-your-portfolio-with-react-vue-and-nextjs-131l"&gt;the Dev.to articles fetch&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edge Functions didn't work
&lt;/h2&gt;

&lt;p&gt;I found this the hard way. Even though Vercel's docs mention &lt;a href="https://vercel.com/docs/frameworks/astro#edge-functions" rel="noopener noreferrer"&gt;Edge Functions don't work natively with Astro&lt;/a&gt;, in the same breath they say you can get it to work by following &lt;a href="https://vercel.com/docs/functions/quickstart" rel="noopener noreferrer"&gt;Functions Quickstart&lt;/a&gt;. But I just couldn't.&lt;/p&gt;

&lt;p&gt;First problem was the &lt;code&gt;vercel&lt;/code&gt; cli. It uses &lt;a href="https://www.npmjs.com/package/ts-node" rel="noopener noreferrer"&gt;ts-node&lt;/a&gt; to compile the functions defined in &lt;code&gt;/api&lt;/code&gt;. For that purpose it needs &lt;code&gt;tsconfig.json&lt;/code&gt; but it couldn't use the Astro's one - it couldn't find the referenced parent tsconfig. &lt;/p&gt;

&lt;p&gt;So I figured I could use the parent config directly. But it turns out Astro's and Vercel's TS configs differ a lot and one can't use 1 config for both. My idea to create separate &lt;code&gt;tsconfig.api.json&lt;/code&gt; didn't meet with success either - Vercel always looks for &lt;code&gt;tsconfig.json&lt;/code&gt; and one can't configure it otherwise.&lt;/p&gt;

&lt;p&gt;At this point I thought this DX wasn't great for such a simple task and I began looking for alternatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Serverless Functions to the rescue
&lt;/h2&gt;

&lt;p&gt;I found &lt;a href="https://vercel.com/docs/frameworks/astro#serverless-functions" rel="noopener noreferrer"&gt;Serverless Functions with a Vercel Adapter&lt;/a&gt; are better supported and easier to &lt;a href="https://vercel.com/docs/frameworks/astro#using-vercel's-features-with-astro" rel="noopener noreferrer"&gt;set up&lt;/a&gt;. Following the link I had to do these steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm astro add vercel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in my &lt;code&gt;astro.config.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&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;astro/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;vercelServerless&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@astrojs/vercel/serverless&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;vercelStatic&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@astrojs/vercel/static&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="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;static&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;vercelServerless&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then I could happily define my functions not in &lt;code&gt;/api&lt;/code&gt; but in pages like &lt;code&gt;src/pages/blogposts.ts&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;GET&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;articlesResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to/api/articles/me&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;api-key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[API_KEY]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;articles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;articlesResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;articles&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;to which I can the just &lt;code&gt;GET /blogposts&lt;/code&gt; and voila, there they are! The bonus is of course that I don't need any server with serverless (duh) and can stay on my Hobby Vercel plan, yay!&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;Of course I now feel naive that I went for Edge Functions at first. They are a different beast compared to Serverless Functions deployment-wise. But the thing is I didn't care; I just wanted some quick and dirty API asap. &lt;/p&gt;

&lt;p&gt;Me not being very experienced in Astro + Vercel definitely made things harder. It took me some time to wrap my head around the Vercel ecosystem and how to put things together. That's why I hope this post helps you if you struggle with the same thing ❤️.&lt;/p&gt;

</description>
      <category>vercel</category>
      <category>astro</category>
      <category>serverless</category>
      <category>portfolio</category>
    </item>
    <item>
      <title>MVC in Frontend is Dead</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Wed, 28 Aug 2024 13:53:54 +0000</pubDate>
      <link>https://dev.to/daelmaak/mvc-in-frontend-is-dead-19ff</link>
      <guid>https://dev.to/daelmaak/mvc-in-frontend-is-dead-19ff</guid>
      <description>&lt;p&gt;I watched a video from Theo about &lt;a href="https://www.youtube.com/watch?v=eMTFzpxR0QQ" rel="noopener noreferrer"&gt;MVC and how it sucks&lt;/a&gt; and it got me thinking. We have been using MVC as the go-to frontend architecture in our company for quite some time. But no one ever questioned if it's the right approach; on the contrary, some people just loved it! &lt;/p&gt;

&lt;p&gt;Since it still remains popular, and I didn't find any solid critique on the web, I decided to write one.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVC what?
&lt;/h2&gt;

&lt;p&gt;By MVC (model-view-controller) I mean the "classic" way of writing frontend apps you might remember from Angular.js (I am old I know).&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%2Fcgt7xgp364com9fcw4hv.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%2Fcgt7xgp364com9fcw4hv.png" alt="MVC overview" width="520" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Short description&lt;/strong&gt;: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The View consists of UI components, let's say built with React.&lt;/li&gt;
&lt;li&gt;When user performs an action, like a click, the View delegates to a Controller which does the heavy lifting - it might create a record in DB by calling an API and update the Model as a result. &lt;/li&gt;
&lt;li&gt;When Model, which holds the application state, updates, the View reflects the changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now when you put it like that, it sounds simple enough so why wouldn't we want to use it? Well, after spending years maintaining MVC apps, I am skeptical MVC is worth the effort. Let's go through:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the main arguments for MVC which I'll mark with ❌ as debunked&lt;/li&gt;
&lt;li&gt;the things MVC generally sucks at, marked with 👎 &lt;/li&gt;
&lt;li&gt;comparison to alternatives like &lt;a href="https://tanstack.com/query/latest" rel="noopener noreferrer"&gt;Tanstack Query&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  MVC makes for a clean code ❌
&lt;/h2&gt;

&lt;p&gt;The assumption is that if you divide your code into neat boxes with distinct responsibilities, it makes the whole codebase better. That sounds logical, but does it really? Put shared state aside and have a look at a simple example: A component for showing and creating posts, talking to a backend API. See how it's handled with and without MVC:&lt;/p&gt;

&lt;h3&gt;
  
  
  MVC
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// View&lt;/span&gt;
&lt;span class="c1"&gt;// Has to be made aware of reactivity, therefore `observer` from MobX&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;observer&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;mobx-react-lite&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;const&lt;/span&gt; &lt;span class="nx"&gt;Posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;model&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="nf"&gt;createEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&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="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PostCreator&lt;/span&gt; &lt;span class="nx"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Controller&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;PostsController&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="k"&gt;private&lt;/span&gt; &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostsModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;fetchPosts&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPostsApi&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;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&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;createdPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPostApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&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;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;createdPost&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;// Model&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;makeObservable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;observable&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;mobx&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;PostsModel&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// has to be reactive, using MobX for that&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;observable&lt;/span&gt; &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Post&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="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;makeObservable&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="p"&gt;}&lt;/span&gt;

  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;action&lt;/span&gt;
  &lt;span class="nf"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;posts&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;posts&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="nd"&gt;action&lt;/span&gt;
  &lt;span class="nf"&gt;addPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;post&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;posts&lt;/span&gt; &lt;span class="o"&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;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;post&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;And now without MVC.&lt;/p&gt;

&lt;h3&gt;
  
  
  No MVC
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&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;Posts&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&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="nf"&gt;createEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchData&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPostsApi&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="nf"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;fetchData&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&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;createdPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPostApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;([...&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createdPost&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="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PostCreator&lt;/span&gt; &lt;span class="nx"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am past the point of calling a code bad just because it's a bit longer, but in this instance the MVC code is 3x times longer 🚩. The more important question is Did it bring any value? Yes, the component is a little cleaner by not having to deal with the asynchronicity, but the rest of the app is quite wordy. As a result, when you add new features and perform code changes, the commits are accordingly bigger.&lt;/p&gt;

&lt;p&gt;As for the No-MVC snippet, I purposefully made it simplistic and didn't use &lt;a href="https://tanstack.com/query/latest" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt; which would make the code even more reasonable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizing Controllers is complex 👎
&lt;/h2&gt;

&lt;p&gt;Over time, your controllers will amass a huge number of dependencies. &lt;/p&gt;

&lt;p&gt;Why? First of all, many components might depend on them to handle their events and get access to the Model data.&lt;/p&gt;

&lt;p&gt;Secondly, controllers are the glue of the app, meaning they directly mutate models to change the state, call services to perform server mutations and selectively expose Model to View. In other words, they hold the complex business logic. &lt;/p&gt;

&lt;p&gt;Inevitably, they become quite heavy and the time comes to split them up. The question is along which lines do you split them: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;per domain &lt;/li&gt;
&lt;li&gt;per view&lt;/li&gt;
&lt;li&gt;per component (Jesus 🙈) ...? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The answer is not obvious and a bad call will lead to sizeable refactorings and rewrites later on. &lt;/p&gt;

&lt;p&gt;As if that wasn't complicated enough, you'll find there are dependencies among controllers - parent controller initializes child controller, instructs it to fetch data at a certain point, child needs to notify parent about new developments etc. It gets very complex very fast just to keep the touted promise of "clean, separate boxes" architecture.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVC doesn't help with Caching 👎
&lt;/h2&gt;

&lt;p&gt;Caching is one of the hardest things to do correctly in CRUD apps and an important problem to solve given today's computation like AI prompts and media generation can be very expensive. Yet, MVC isn't helpful here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostsController&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="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PostsModel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{...}&lt;/span&gt;

  &lt;span class="nf"&gt;fetchPosts&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;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPostsApi&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;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;posts&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;If 3 separate components call &lt;code&gt;fetchPosts()&lt;/code&gt;, the fetch happens 3 times although logically just 1 fetch would have sufficed. You probably don't want that, so how do you optimize? There are basically 2 options with MVC:&lt;/p&gt;

&lt;h3&gt;
  
  
  Ad-hoc caching
&lt;/h3&gt;

&lt;p&gt;Write code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; 
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPostsApi&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;model&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setPosts&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;posts&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;Which is brittle, not centrally handled and very limited in its capabilities. If you'd like stale timeouts, cache evictions etc., you'd have to write it yourself or bring a library onboard which handles it. That doesn't sound so bad, but given MVC separates the Model from the other layers, you'd still need a lot of code for the V and C to have some say in the M's caching state.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data fetching caching
&lt;/h3&gt;

&lt;p&gt;The other option is caching on the data fetching layer, either using Service Worker (which isn't very flexible solution) or some fetch-caching lib. Same as with the ad-hoc caching above however, the problem remains that the fetch calls and the models are disconnected and the controllers need to keep them in sync at all times.&lt;/p&gt;

&lt;h2&gt;
  
  
  MVC makes for a Better Testing ❌
&lt;/h2&gt;

&lt;p&gt;The argument tends to be two-fold here: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Because the app layers are so well defined, it makes the (unit) testing cleaner and therefore easier.&lt;/li&gt;
&lt;li&gt;Since View is devoid of its own state, one should hardly need the View layer to be unit and integration tested. That saves testing complexity by removing the UI lib from the equation.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let's see how it is in reality.&lt;/p&gt;

&lt;h3&gt;
  
  
  It makes unit testing harder
&lt;/h3&gt;

&lt;p&gt;The first argument is a false promise. Since the View is dull (remember, it's the Controller calling the shots) and the Model usually doesn't contain much business logic (Controllers do), there indeed isn't much to unit test in the View. &lt;/p&gt;

&lt;p&gt;However, that means all weight is on Controllers, with their many dependencies and complex logic. If you want to unit test them, you'll have a lot of mocking to do which in turn makes testing very labourious and the testing conditions so unrealistic that it's borderline useless. &lt;/p&gt;

&lt;p&gt;The other option is to test Controllers only in integration tests where the bulk of the app is initialized, but that requires a proper set up and upfront planning to architect the app accordingly.&lt;/p&gt;

&lt;h3&gt;
  
  
  No you can't just forget about UI in tests
&lt;/h3&gt;

&lt;p&gt;The second argument about leaving out the View from tests is even bigger Fata Morgana. Even if the View layer was "dull", there is always some logic in there whether you want it or not - handling user events, conditional display of content, UI updates through state changes, reactivity, you name it. And there can be bugs in all of these, possibly making the UI unusable. &lt;/p&gt;

&lt;p&gt;That only shows that leaving out UI unit tests is a gamble, and E2E can't fully offset it.&lt;/p&gt;

&lt;h3&gt;
  
  
  But then I need to include React in my tests
&lt;/h3&gt;

&lt;p&gt;So? It will bring you and your tests closer to your users. It's a breeze to test with the &lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt; and given how many starter tools we have nowadays, it's no problem to include the UI framework in your tests as well.&lt;/p&gt;

&lt;p&gt;I absolutely love this notion from a random person (don't remember where I saw it :/) on the internet on the topic of performance: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If the added React layer significantly increases the tests execution time, it's not the tests that are to blame, it's your bloated UI.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  State is in the Model 👎
&lt;/h2&gt;

&lt;p&gt;Separating state away from components is completely arbitrary. Don't get me wrong, shared state of course should be made accessible to components that need it. But if a state concerns just one part of the view, it should be co-located with its component, simple as that.&lt;/p&gt;

&lt;p&gt;The reason is that it's much simpler to grasp how a component and its state influence each other if the two are in a direct relationship, even in the same file. &lt;/p&gt;

&lt;p&gt;In MVC, the View has to trust the Controller to give it the right state and handle the state mutations correctly since it has no direct influence on the Model itself. And of course you need to inspect at least 3 files instead of 1 to understand how components and state interact. This becomes even more evident when you throw state management solution like Redux into the mix.&lt;/p&gt;

&lt;h2&gt;
  
  
  State should be in the Model ONLY ❌
&lt;/h2&gt;

&lt;p&gt;This is such a strict measure and no wonder &lt;a class="mentioned-user" href="https://dev.to/markdalgleish"&gt;@markdalgleish&lt;/a&gt; apologized for enforcing it in the distant past through a &lt;a href="https://x.com/markdalgleish/status/1712035562595758384" rel="noopener noreferrer"&gt;Lint rule&lt;/a&gt;. I am firmly convinced putting ALL state to central places and interacting with it only through controllers leads to bloated, hard to understand and hard to change code.&lt;/p&gt;

&lt;h2&gt;
  
  
  You can easily switch to different UI framework ❌
&lt;/h2&gt;

&lt;p&gt;Emphasis on the word &lt;em&gt;framework&lt;/em&gt;. Frameworks are usually opinionated about the app architecture, and use their own kinds of primitives for building apps. Take Angular's DI and RxJS, or Solid's Signals as an example.&lt;/p&gt;

&lt;p&gt;If you use MVC and want to do a rewrite, say, from React -&amp;gt; Angular, well first why on Earth would you do that, and second, Angular is so different from React that you'd probably have to do a large rewrite. Such a rewrite would ripple through all the parts of your MVC architecture, even wannabe framework-agnostic controllers. &lt;/p&gt;

&lt;h3&gt;
  
  
  Is the touted case for an easy rewrite even valid?
&lt;/h3&gt;

&lt;p&gt;It's questionable if a rewrite of such parameters where you only swap the UI framework and leave the rest largely intact isn't just a chimera. The most common reasons for a rewrite are in my experience:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The need for a refresh of a legacy UI, providing new and updated features. Here, it's usually easier to start from scratch due to legacy baggage.&lt;/li&gt;
&lt;li&gt;Rewrite out of frustration with unmaintainable code base, leading to a complete overhaul of the app's architecture.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Neither would leave the MVC architecture intact and it turns this supposed benefit on its head.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what's the better alternative? ✅
&lt;/h2&gt;

&lt;p&gt;I am tempted to say anything else than MVC, but I don't want to overreach. I'd say a very solid approach is to use the already mentioned &lt;a href="https://tanstack.com/query/latest/docs/framework/react/overview" rel="noopener noreferrer"&gt;TanStack Query&lt;/a&gt;, since it solves most if not all the discussed problems. Let's see some code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useQuery&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;@tanstack/react-query&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Posts&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;refetch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useQuery&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;queryKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;queryFn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fetchPostsApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPostApi&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;refetch&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="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PostCreator&lt;/span&gt; &lt;span class="nx"&gt;onCreate&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createPost&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;article&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/article&amp;gt;&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So you can immediately see that instead of interacting with a controller or fetching data directly, I define a &lt;code&gt;Query&lt;/code&gt; which does that for me. When I create a new post, there is a convenient &lt;code&gt;refetch&lt;/code&gt; method that performs the query again.&lt;/p&gt;

&lt;p&gt;Now there is a lot to talk about regarding TanStack Query but I'll concentrate only on the points discussed above.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code being clean ✅
&lt;/h3&gt;

&lt;p&gt;I say the code is much cleaner by the mere fact that we got rid of Controller completely and in this instance of the Model as well. Of course if you need to extract business logic or state and make it shared, do that, but there is no reason to follow the rigid MVC structure.&lt;br&gt;
And of course, as a bonus, there is much less code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Organizing Controllers ✅
&lt;/h3&gt;

&lt;p&gt;Not an issue anymore, Controllers are gone.&lt;/p&gt;
&lt;h3&gt;
  
  
  Caching ✅
&lt;/h3&gt;

&lt;p&gt;This is a staple feature of TanStack Query. All requests are recorded, cached and deduplicated automatically as needed. You can set cache expiration time, invalidate, refetch and much more, very easily, just check their &lt;a href="https://tanstack.com/query/latest/docs/framework/react/quick-start" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. &lt;/p&gt;
&lt;h3&gt;
  
  
  Testing ✅
&lt;/h3&gt;

&lt;p&gt;Testing is pretty easy I'd say, as only 2 steps are required in the particular architecture I am using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchPostsSpy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;vi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postsApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetchPostsApi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;render&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="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;QueryClientProvider&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;queryClient&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Posts&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mocking the API to provide dummy data and providing the &lt;code&gt;QueryClient&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You only test what the particular component needs, nothing more, no big chunk like with MVC Controllers.&lt;/p&gt;

&lt;h3&gt;
  
  
  State placement and syncing ✅
&lt;/h3&gt;

&lt;p&gt;State is co-located with the components through the &lt;code&gt;Query&lt;/code&gt; and synced automatically with the data (state) provided by the backend, all in one objects.&lt;/p&gt;

&lt;p&gt;This is of course not to say that you should have all your business logic in your components or that &lt;em&gt;all&lt;/em&gt; state should be inside components as well. On the contrary, it absolutely makes sense to extract this where needed. My point is however that this should be done with judgement in the simplest way possible rather than blindly follow MVC &lt;em&gt;everywhere&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;I am pretty certain the case for MVC in the Frontend is weak and there is no good reason to use it, as there are much superior alternatives. &lt;/p&gt;

&lt;p&gt;I'd love to know what you think, do you like using it, or did you wave it good bye and never looked back?&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>architecture</category>
      <category>frontend</category>
    </item>
    <item>
      <title>5 most frequent A11y Mistakes</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Tue, 30 Jul 2024 08:56:45 +0000</pubDate>
      <link>https://dev.to/daelmaak/5-most-frequent-a11y-mistakes-2f65</link>
      <guid>https://dev.to/daelmaak/5-most-frequent-a11y-mistakes-2f65</guid>
      <description>&lt;h2&gt;
  
  
  1. Always relying on &lt;code&gt;aria-label&lt;/code&gt; 😳
&lt;/h2&gt;

&lt;p&gt;If you are guilty of this mistake, I don't blame you since I fell into the same trap myself. People are only too readily using &lt;code&gt;aria-label&lt;/code&gt; whenever they want to provide additional context, but they underestimate how careful they have to be with it.&lt;/p&gt;

&lt;p&gt;First of all you might not even need it at all. Consider this:&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;section&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Quiz answers"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Quiz answers&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example is pretty simplistic, but it goes to show that your HTML structure is often enough to provide screen reader (SR) users the needed context. A &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;, as MDN and the &lt;a href="https://html.spec.whatwg.org/multipage/sections.html#the-section-element" rel="noopener noreferrer"&gt;HTML Standard&lt;/a&gt; tell us, is usually accompanied by a heading and SRs rely on that. In practice, even when a SR user navigates a webpage by regions instead of headings (regions are special areas on the page like &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; etc.) the &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; will be read together with the &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; by the SR even without the &lt;code&gt;aria-label&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But there is a much more sinister side of things than just needlessly slapping useless aria-labels everywhere, they can actually betray you! 😕 Check this out:&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;ul&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"Option 1 is the correct answer, and you did not vote for it"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;aria-hidden&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Option 1&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks completely valid right? We provide additional context in &lt;code&gt;aria-label&lt;/code&gt; and hide the &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; because we don't want 'Option 1' to be read twice. But guess what, the whole list will be completely ignored in both NVDA and JAWS screen readers! Now why the hell is that?&lt;/p&gt;

&lt;p&gt;The issue has 2 components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The list item has no SR-visible content thanks to &lt;code&gt;aria-hidden&lt;/code&gt;. That's I guess anticipated, but...&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;aria-label&lt;/code&gt;, which was supposed to work around the hidden content, is actually invalid on &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Not only isn't &lt;code&gt;aria-label&lt;/code&gt; compatible with the list item element, it usually can't be used on such ubiquitous elements like &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; either! I bet you didn't see that coming, and I certainly hadn't in the past.&lt;/p&gt;

&lt;p&gt;So when can you use it then? MDN &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-label" rel="noopener noreferrer"&gt;says it clearly&lt;/a&gt; (emphasis mine):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;... in practice however, it is supported only on &lt;strong&gt;interactive&lt;/strong&gt; elements, &lt;strong&gt;widgets&lt;/strong&gt;, &lt;strong&gt;landmarks&lt;/strong&gt;, &lt;strong&gt;images&lt;/strong&gt;, and &lt;strong&gt;iframes&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Meaning that if your &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; has eg. a &lt;code&gt;role="button"&lt;/code&gt;, aria-label is gonna work, but if it is plain old div, the aria-label will be disregarded. And as my example shows, this can go beyond the SR not providing the additional context, it can actually lead to a whole section of content being hidden from the SR user when particular conditions are met.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Disabling form elements
&lt;/h2&gt;

&lt;p&gt;A lot has been written on this topic and I recommend Adrian Roselli's &lt;a href="https://adrianroselli.com/2024/02/dont-disable-form-controls.html" rel="noopener noreferrer"&gt;article&lt;/a&gt; for summary. I have basically 2 issues with disabling form elements which imo are relevant to both SR and non-SR users.&lt;/p&gt;

&lt;h3&gt;
  
  
  They are hard to see 🙈
&lt;/h3&gt;

&lt;p&gt;For some reason, Web Content Accessibility Guidelines (WCAG) don't require the same color contrast standard for disabled elements as for the rest of the page, which then allows for such designs: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnt5bu38g3mha52bhsqok.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnt5bu38g3mha52bhsqok.png" alt="Not a very visible disabled button" width="452" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If your sight is 20/20 you'll have no problem reading this button but so many other people just won't see the text and neither even the button itself, given its low contrast with the white background.&lt;/p&gt;

&lt;h3&gt;
  
  
  They provide no explanation
&lt;/h3&gt;

&lt;p&gt;Ask yourself an honest question: How often, when you disable form elements or any controls for that matter, do you also provide an explanation on why they are disabled? Exactly. &lt;/p&gt;

&lt;p&gt;It's super annoying and confusing not to know why and not just for assistive tech users, though it of course hits them the hardest. It might be logical and perfectly understandable to us, programmers, that eg. a submit button should stay disabled until all form fields are valid, but it's not as cut and dried for our users.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Underestimating SR differences
&lt;/h2&gt;

&lt;p&gt;There are big differences between SRs on multiple levels. The obvious one is the way SRs read webpages. Remember that &lt;code&gt;aria-label&lt;/code&gt; gotcha from #1 above? It actually worked in VoiceOver on Mac and the &lt;code&gt;aria-label&lt;/code&gt;ed content was read just fine but it was completely broken in the Windows SRs - NVDA and JAWS. Other times when I coded the first version of one highly interactive feature and used primarily NVDA to develop it, it worked great there but didn't work at all with VoiceOver.&lt;/p&gt;

&lt;p&gt;In terms of development, your best bet is to follow the &lt;a href="https://www.w3.org/TR/wai-aria/" rel="noopener noreferrer"&gt;WAI-ARIA standard&lt;/a&gt; and use the &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/" rel="noopener noreferrer"&gt;Authoring Practices Guide&lt;/a&gt; and you should be in the clear in most cases. &lt;/p&gt;

&lt;p&gt;But that doesn't mean you can skip the testing and you should absolutely test on multiple platforms and SRs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows, ideally both NVDA and JAWS due to their big market share, based on the &lt;a href="https://webaim.org/blog/screen-reader-user-survey-10-results/" rel="noopener noreferrer"&gt;WebAIM 2024 Survey&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Mac with VoiceOver&lt;/li&gt;
&lt;li&gt;iOS with VoiceOver&lt;/li&gt;
&lt;li&gt;Android, eg. with Talkback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd say this is the minimal suite but I totally understand if it's too much for one developer to do every day, because of course it is. What helps is that over time you'll learn which things require a full (re)test and of course your company's a11y testing/auditing process should have your back, but that's its own big topic which I won't expand on here.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Not knowing how users use SR 🤔
&lt;/h2&gt;

&lt;p&gt;I see it a lot with people who are just starting to play with screen readers, be it developers or designers, having a very fuzzy idea of how one navigates the presented content beyond pressing the Tab key. This of course leads to less than ideal implementations, same as with building a product and not understanding your audience.&lt;/p&gt;

&lt;p&gt;There are many ways to navigate a webpage or any other content with screen readers. &lt;a href="https://webaim.org/projects/screenreadersurvey10/#finding" rel="noopener noreferrer"&gt;Most people use headings&lt;/a&gt; to make sense of the content. Jumping to specific regions, navigating by links, forms or the Find feature are all important tools for the SR users. &lt;/p&gt;

&lt;p&gt;But it's not just about which content "anchors" can be utilized. If we come back to the SR differences for a second, we'll discover very different techniques of navigating. VoiceOver users can use a so called &lt;a href="https://support.apple.com/en-gb/guide/voiceover/mchlp2719/mac" rel="noopener noreferrer"&gt;Rotor&lt;/a&gt; to switch between different types of "anchors" (regions, links, headings, ...) and even search among them to reach their desired destination. NVDA on the other hand works &lt;a href="https://www.youtube.com/watch?v=Jao3s_CwdRU" rel="noopener noreferrer"&gt;very different to VO&lt;/a&gt; in that it doesn't operate in one all-encompassing mode but rather in multiple and when a user eg. wants to operate a form, she needs to switch from browsing to interactive.&lt;/p&gt;

&lt;p&gt;SRs on mobile are their own chapter because users operate them very differently to desktop and without going into much detail, they really put things into perspective (more on that in #5).&lt;/p&gt;

&lt;p&gt;Understanding all this is key in order to implement accessible apps and it will help you know what and why you are doing when addressing a11y issues. Luckily, there are great resources on getting this kind of understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://webaim.org/projects/screenreadersurvey10/" rel="noopener noreferrer"&gt;WebAIM's 2024 Screen Reader survey&lt;/a&gt; detailing how and which SRs people use&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/people-use-web/user-stories/" rel="noopener noreferrer"&gt;Stories of assistive tech users&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Channels like &lt;a href="https://www.youtube.com/@theblindlife" rel="noopener noreferrer"&gt;Blind Life&lt;/a&gt; going into detail on how tech is really used by users with disability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Misunderstanding the purpose of the Tab key
&lt;/h2&gt;

&lt;p&gt;This is kinda my favorite one since I see it quite a lot and I also feel understanding it will make the lives of us all so much easier. So let's join hands and say it all together: &lt;strong&gt;Tab key is not for navigating all interactive elements!&lt;/strong&gt; 🤯&lt;/p&gt;

&lt;p&gt;Feeling funny now? Once the dizziness passes, you might be thinking "What the hell did I just say?". It might make no sense to you. But in reality it makes a lot of sense. Consider the example below; just click into the Codepen (I stole it from MDN) and Tab all the way through to the current tabpanel (content of the current tab).&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/daelmaak/embed/mdZRrEv?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;That took a little too many Tab presses didn't it? Sure, all tabs are interactive and should be focusable but did we need to go through all of them? And now imagine there were 10 tabs, not 5. Or even better, imagine you'd be trying to get over a list of 1000 emojis to reach a content below it. And imagine doing that on mobile, where Tab is performed by swiping left and right! 🤯 That'd be more than a little tedious, right?&lt;/p&gt;

&lt;p&gt;This example gets to show that &lt;strong&gt;it's actually NOT the purpose of the Tab key to go through all interactive elements on the page&lt;/strong&gt;, but rather browse quickly through the page's content. If the user decides to interact with the content like the tablist from above, she can do so with the arrow keys. &lt;/p&gt;

&lt;p&gt;It's like walking down a street, knocking on the door of the houses alongside it and deciding on the spot whether to enter or go to the next one. Going from house to house is the Tab key pressing and entering the house is the Arrow keys (don't quote me on that once they apprehend you for trespassing).&lt;/p&gt;

&lt;p&gt;So this is how it should look in practice, the first Tab keypress moves focus to the currently active tab, second Tab press goes to the tabpanel (tab content). Neat!&lt;/p&gt;

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

&lt;p&gt;Again, a great resource on how to get this kind of interactivity is the &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/tabs/" rel="noopener noreferrer"&gt;Authoring Guidelines from W3C&lt;/a&gt; and I can't recommend it highly enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it!
&lt;/h2&gt;

&lt;p&gt;Alright, lemme know which a11y transgression bothers you the most and what you would put into your Top 5 list!&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>html</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Solid's Resource is great!</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Tue, 07 May 2024 09:21:52 +0000</pubDate>
      <link>https://dev.to/daelmaak/solids-resource-is-great-1je7</link>
      <guid>https://dev.to/daelmaak/solids-resource-is-great-1je7</guid>
      <description>&lt;p&gt;When you say Solid, Signal usually comes to mind, but there are many more great APIs there. One of them is &lt;a href="https://www.solidjs.com/tutorial/async_resources" rel="noopener noreferrer"&gt;Resource&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchPosts&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="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/posts`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;Number&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="na"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, Resource basically does 2 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Manages an async operation, like a fetch&lt;/li&gt;
&lt;li&gt;Binds the result of that operation to Solid's reactive model&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Therefore, it makes an async operation appear synchronous. See? I haven't used &lt;code&gt;await&lt;/code&gt; anywhere in the App component.&lt;/p&gt;

&lt;p&gt;You can also connect the Resource to a Signal. Every time the latter changes, Resource performs the async operation again.&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;fetchPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://jsonplaceholder.typicode.com/posts/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPostId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSignal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onOpenPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;setPostId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty neat. However, sometimes you need a bit more control over the encapsulated data, and that's where &lt;code&gt;refetch&lt;/code&gt; and &lt;code&gt;mutate&lt;/code&gt; come in.&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;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPostId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSignal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;refetch&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchPosts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onCreatePost&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;newPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPostInDB&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;refetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updatePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedPost&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updatePostInDB&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updatedPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldPost&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="nx"&gt;oldPost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;updatedPost&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;As their names tell, &lt;code&gt;refetch&lt;/code&gt; makes the Resource, in our case, call the backend API again. This can be handy anytime it's more consistent to get fresh new data from the backend rather than modify it in place on the frontend.&lt;br&gt;
&lt;code&gt;mutate&lt;/code&gt; on the other hand lets you modify the data in place without calling backend API. The one thing I'd love and I am missing at the moment would be to have the same capabilities to mutate the Resource as with Solid &lt;a href="https://docs.solidjs.com/concepts/stores#modifying-store-values" rel="noopener noreferrer"&gt;Stores&lt;/a&gt; as that would make the mutations much easier to perform.&lt;/p&gt;

&lt;p&gt;To wrap the nice features up, there are &lt;code&gt;loading&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt; properties on the Signal produced by Resource which make it super easy to track the fetch's status:&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;App&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPostId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSignal&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchPost&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;post&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loading&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are more interesting Resource options and properties like &lt;code&gt;state&lt;/code&gt; which you can find in the &lt;a href="https://docs.solidjs.com/reference/basic-reactivity/create-resource" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;When I was listing all those advantages of Solid's Resource, I bet many of you thought Tanstack Query. And they are indeed similar in many ways, although the latter is much more powerful. That's why it's so nice there is an official &lt;a href="https://tanstack.com/query/v4/docs/framework/solid/overview" rel="noopener noreferrer"&gt;distribution for Solid&lt;/a&gt; as well!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add @tanstack/solid-query
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd say use the built-in Resource as long as you can and only when you need to manage your queries in a more complex fashion switch to Solid Query.&lt;/p&gt;

&lt;p&gt;There was some &lt;a href="https://www.reddit.com/r/solidjs/comments/170xcnv/solidjs_mobx_is_amazing/" rel="noopener noreferrer"&gt;talk of Solid + MobX&lt;/a&gt; in the past but to me, in case of async resources, I still prefer Solid Resource since it encapsulates both the fetch and the data in one easy to manipulate object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;That's all nice, you might say, but having those Resources in components is not great, I'd like to extract them outside to make them more testable and maintainable. Well, there is nothing stopping you form doing exactly that!&lt;/p&gt;

&lt;p&gt;I like to put them outside components like this:&lt;br&gt;
&lt;code&gt;post-resource.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPostId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createSignal&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;number&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;postResource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createResource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fetchPost&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;const&lt;/span&gt; &lt;span class="nx"&gt;getPost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&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="nf"&gt;setPostId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;postResource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updatePost&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;postPatch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;&amp;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;success&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updatePost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postPatch&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;mutate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;postResource&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="nf"&gt;mutate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;p&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="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;postPatch&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;And then in the component I just use those exposed functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PostDetail&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;postId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;post&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;postId&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;That means you can build an entire frontend architecture around the Resources! No need to sync API calls with Redux or other complicated solutions, you can just use the Resource and expose as much or little as you want.&lt;/p&gt;

&lt;p&gt;One benefit of extracting the Resources from components is that they can then be &lt;a href="https://www.solidjs.com/guides/testing" rel="noopener noreferrer"&gt;easily tested&lt;/a&gt; on their own. The only thing you should be careful about is not to forget &lt;code&gt;createRoot&lt;/code&gt;. It sets up reactive context inside which updates to reactive state are detected. This context normally gets set up when calling &lt;code&gt;render&lt;/code&gt;, but we are not rendering anything when testing just Resources. Therefore, you should wrap the part that creates Resources in &lt;code&gt;createRoot&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resource fetches from api&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;postResource&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&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="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;initResources&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;testApi&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;If you are wondering what the &lt;code&gt;setup&lt;/code&gt; function is about, check out my article &lt;a href="https://dev.to/daelmaak/5-steps-for-better-unitintegration-testing-4ndc"&gt;5 steps to better unit/integration testing&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's it folks
&lt;/h2&gt;

&lt;p&gt;How do you like Solid Resource so far, and do you use it yourself? Which other architecture do you use in your Solid apps? I'd love to know!&lt;/p&gt;

</description>
      <category>solidjs</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>5 steps to better unit/integration testing</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Sun, 07 Apr 2024 19:49:35 +0000</pubDate>
      <link>https://dev.to/daelmaak/5-steps-for-better-unitintegration-testing-4ndc</link>
      <guid>https://dev.to/daelmaak/5-steps-for-better-unitintegration-testing-4ndc</guid>
      <description>&lt;h2&gt;
  
  
  1. Use setup functions
&lt;/h2&gt;

&lt;p&gt;Setup functions are much better pattern than using test lifecycle methods like &lt;code&gt;beforeEach&lt;/code&gt;, &lt;code&gt;beforeAll&lt;/code&gt; and the rest. What do I mean by setup functions?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should do something with current user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&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;userApi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserTestApi&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;userService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userApi&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userService&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;They do as their name says, they set up everything that's needed to run your tests, including mocks, stubs, populate models, you name it. &lt;/p&gt;

&lt;p&gt;Why is using test lifecycle methods an anti-pattern?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They create hidden dependencies for all test they apply to.&lt;/li&gt;
&lt;li&gt;They are inflexible. If you need different behavior for some of your tests, you have to put them in a different &lt;code&gt;describe&lt;/code&gt; with a different lifecycle method implementation. Their number can really explode if the test vary a lot.&lt;/li&gt;
&lt;li&gt;They are rigid, since they can't be parameterized from the individual tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Setup functions don't suffer from these problems. On the contrary, they allow to overcome them easily. In the same order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Hidden dependencies - not a problem. You can have various setup methods, or just parameterize them and then the tests position in the file doesn't matter. You can move it elsewhere, even outside of top level &lt;code&gt;describe&lt;/code&gt; and it won't break the test.&lt;/li&gt;
&lt;li&gt;They are flexible for the reasons mentioned above.&lt;/li&gt;
&lt;li&gt;The aren't rigid since you can pass unique arguments from inside each of your tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  2. Prevent nesting
&lt;/h2&gt;

&lt;p&gt;Nesting is one of the most wanted bandits in any kind of code, as it makes code less readable and harder to reason about. This is no different for tests. If you nest multiple &lt;code&gt;describes&lt;/code&gt; and aggravate the problem by layering test lifecycle methods as well, you've got a serious problem on your hands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;My component&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;beforeAll&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in read-only mode&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...});&lt;/span&gt;

    &lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;on touch device&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...});&lt;/span&gt;

      &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;poor me if you want to change the setup!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{...});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reading and thus maintaining this code is hell. If you want to move the actual test case elsewhere, or change the way it's set up, good luck!&lt;/p&gt;

&lt;p&gt;The solution is easy. Don't nest, don't divide the setup into multiple layers, just keep it shallow as much as you can.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Treat your tests same as your production code
&lt;/h2&gt;

&lt;p&gt;We programmers are used to caring a lot about our production code. We refactor it, divide it into neat modules, extract reusable parts, make sure it's commented and nicely readable. But honestly, do you treat your tests the same way? The truth is that you probably don't, at least according to my experience from many companies.&lt;/p&gt;

&lt;p&gt;We programmers often treat tests as necessary evil that we want to be done with asap. This is partly due to the tests often being a kind of an afterthought. Lot of people tend to write production code, refactor it, test it manually, and only when thinking they are finally done do they realize they should write tests for their code as well.&lt;/p&gt;

&lt;p&gt;I am not the kind of guy who will tell you to do &lt;em&gt;TDD&lt;/em&gt;, because in many situations it simply doesn't make sense. If you want to improve however, be more conscious of the principles you apply to your production code and apply them to your tests too. Are your tests not DRY? Just extract common steps into reusable functions. Are your test files too long and unwieldy? Split them up into multiple logical units (files). And refactor your tests every time you feel like you don't know understand what the hell is going on there.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Find the right balance
&lt;/h2&gt;

&lt;p&gt;Finally getting to the juicy stuff. This might be controversial, but I don't care. Let's just forget once and for all about the mythical &lt;strong&gt;100% test coverage&lt;/strong&gt;. It's a lie and you will never achieve it, whatever your code coverage tool or Uncle Bob says. Even if people accept this, they sometimes hold it as an ideal which they strive towards. I think this leads only to a lot of irrelevant and/or unmaintained tests and therefore to a great time waste. &lt;/p&gt;

&lt;p&gt;The bigger your project, the more impractical this ideal is. The reality is that you have to prioritize the same way as you would with the rest of your code base and your project in general. You can't write test for everything the same way as you can't implement all the features you'd like nor perform all the refactorings you wish.&lt;/p&gt;

&lt;p&gt;There is an &lt;strong&gt;important caveat&lt;/strong&gt; to this though. Most of us work on projects that are not terribly important, sorry, and the agility in development and good real-time monitoring is of more value for us than high code coverage. That said, there are programs that people's lives depend on, and any mistake can be fatal to its users. This can be software used in aviation, medicine or weapon systems. It goes without saying, that making sure the program works correctly is of essence and testing everything is absolutely necessary.&lt;/p&gt;

&lt;p&gt;So the takeaway is following: Judge the importance and ROI of your tests, but most importantly use common sense, and choose your desired coverage accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Prefer integration over unit
&lt;/h2&gt;

&lt;p&gt;Again, this is highly contextual, but in my experience for most apps created today, unit tests make less sense. If you have a unit, be it class or function, which does something either very technically involved, or encapsulates important business logic on its own, unit test is useful. But let's be frank here, most of the time, our units are not that interesting, and creating unit tests for them is waste of time.&lt;/p&gt;

&lt;p&gt;One other aspect is that if you want to change your unit, even if you took great care writing your test properly, you are probably gonna break it. And we change units often. There is nothing worse than being slowed down by tests breaking left and right when you know it's just the implementation changing, not the resulting system behaviour. Such DX often leads to people ditching tests altogether.&lt;/p&gt;

&lt;p&gt;That's the reason I prefer integration tests, ideally such that create the whole app in headless mode, so without the UI, but otherwise with full functionality! Here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should sign in user&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userController&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;signIn&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toBeDefined&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;initTestApp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this setup, I like to test the high level system behaviour instead of the implementation details. Because of this architecture, if I change my units, but the resulting behaviour is still the same, the integration tests don't care. I can change the &lt;code&gt;signIn&lt;/code&gt;'s implementation, but as long as it populates the current user in the model, the test is still gonna pass.&lt;/p&gt;

&lt;p&gt;But this is not all! By using the holistic approach instead of the pretty low-level focus on units, we are testing much closer to what really matters to users - that the system works as a whole. Which means such tests bring much more value than unit tests, which exist in completely mocked and therefore detached environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  That's all folks
&lt;/h2&gt;

&lt;p&gt;Let me know if you agree or if your approach is different. I am happy to discuss.&lt;/p&gt;

</description>
      <category>test</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>5 Most common AB Testing Mistakes</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Fri, 22 Mar 2024 09:17:07 +0000</pubDate>
      <link>https://dev.to/daelmaak/5-most-common-ab-testing-mistakes-4bk1</link>
      <guid>https://dev.to/daelmaak/5-most-common-ab-testing-mistakes-4bk1</guid>
      <description>&lt;p&gt;Running AB tests seems easy at first. After all, you just divide users into 2 groups and compare the results, right? Not quite. Designing, running and evaluating many AB tests, I realized that as many things in life, it's not that simple. Let's have a look then at how to identify some big mistakes you might be making and prevent them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concluding tests too early
&lt;/h2&gt;

&lt;p&gt;This happens when people get excited about &lt;em&gt;preliminary&lt;/em&gt; results before the AB test reaches statistical significance. In other words, it happens all the time. And it's not like people don't know they should be patient, but it's hard to get there (same with holding stocks and watching the prices fall... yikes).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetecg9al4s8gv5to7oy4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fetecg9al4s8gv5to7oy4.jpg" alt="Waiting for test results can be long" width="772" height="558"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is especially true if you are using &lt;a href="https://www.dynamicyield.com/lesson/frequentists-approach-to-ab-testing/" rel="noopener noreferrer"&gt;Frequentist approach&lt;/a&gt;, and Evan Miller provides great reasoning for why it's bad in his &lt;a href="https://www.evanmiller.org/how-not-to-run-an-ab-test.html" rel="noopener noreferrer"&gt;article&lt;/a&gt;. Basically, Frequentist approach requires reaching a &lt;a href="https://www.evanmiller.org/ab-testing/sample-size.html" rel="noopener noreferrer"&gt;predetermined&lt;/a&gt; sample size (typically users in both Treat and Control variant), otherwise the results can't be considered trustworthy, and the chance of picking the wrong variant increases from the standard 5% up to 27.5%. Quite a high false positive rate, isn't it?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.dynamicyield.com/lesson/bayesian-approach-to-ab-testing/" rel="noopener noreferrer"&gt;Bayesian&lt;/a&gt; is much better in this regard as there is no predetermined sample size you need to wait for, though you still should respect relevant seasonality, typically weekly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ignoring Twyman's Law
&lt;/h2&gt;

&lt;p&gt;This happens to everyone who's starting with AB tests. You run it, are careful not to fall into the trap of concluding it too early, check the results ... and they are amazing. The data shows your feature is a great success!&lt;/p&gt;

&lt;p&gt;Unless it isn't. It turns out, most of the time, the results are not significant and there is a bug in play. Time for &lt;a href="https://en.wikipedia.org/wiki/Twyman%27s_law" rel="noopener noreferrer"&gt;Twyman's Law&lt;/a&gt; citation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Any figure that looks interesting or different is usually wrong"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Most likely, either the bug is in your feature, tracking, the way you evaluate the AB test or just the experiment design. Either way, you need to double check before you present your results to the rest of the company, which is something I wish I knew earlier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bhcd8mkc5aoxjlsbdnt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4bhcd8mkc5aoxjlsbdnt.jpg" alt="Awkward look" width="757" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  No Triggering
&lt;/h2&gt;

&lt;p&gt;Imagine you have a site which both anonymous and registered users can visit, and you introduce a new feature &lt;em&gt;only&lt;/em&gt; for the registered users there. The act of making sure you evaluate the experiment only with users who were impacted (or could have been if they weren't in control variant) is called Triggering.&lt;/p&gt;

&lt;p&gt;Without it, you'd factor in also anonymous users which would dillute the results, decrease sensitivity and prolong the test runtime, if not make it impossible to evaluate properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Incomparable variants
&lt;/h2&gt;

&lt;p&gt;Let's imagine you introduce a new banner saying "20% OFF" in your online shop, but, and this is important, it's meant only for new users on their first purchase.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxb1oskdh32w7tkr9sec.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flxb1oskdh32w7tkr9sec.png" alt="Introducing banner in treatment group" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tracking users in Treatment is easy; if the banner gets displayed, you know they entered the experiment. So far so good.&lt;/p&gt;

&lt;p&gt;But when does the Control enter the experiment? After all, there is no banner to track. If you factor in all users, both new and existing, you'll get the No Triggering problem from above, but even worse, only for your Control group now. So you end up comparing apples to oranges, and will get invalid results.&lt;/p&gt;

&lt;p&gt;The solution is to take the same Treatment condition, apply it in Control and track as if the new banner was displayed for Control too, but without actually displaying it. This technique is called Counterfactual tracking and is indespensable in such scenarios.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sample ratio mismatch
&lt;/h2&gt;

&lt;p&gt;SRM describes a situation when you have, let's say, 2 variants - Control and Treat - but there is a big discrepancy in user allocation between them, perhaps 60:40. Same as with AB test results, this discrepancy can be either insignificant, in which case you are fine, or significant and then you have a problem.&lt;/p&gt;

&lt;p&gt;Having SRM means there is probably a bias in how you allocate users to the A/B/n variants and it can have a significant impact on your results to the point of invalidating the AB test completely. Finding out its root cause is key.&lt;/p&gt;




&lt;p&gt;While writing this piece I discovered there are many more AB testing pitfalls so maybe I'll do a part 2. Let me know in the comments if you made those mistakes yourself and which one tripped you up the most.&lt;/p&gt;

</description>
      <category>product</category>
      <category>analytics</category>
      <category>webdev</category>
      <category>ux</category>
    </item>
    <item>
      <title>The most anticipated JS conference in 2024, for you</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Wed, 06 Mar 2024 14:13:07 +0000</pubDate>
      <link>https://dev.to/daelmaak/the-most-anticipated-js-conference-in-2024-for-you-llm</link>
      <guid>https://dev.to/daelmaak/the-most-anticipated-js-conference-in-2024-for-you-llm</guid>
      <description>&lt;p&gt;I haven't been to a JS conference for a while, and I'd like to attend one or two in 2024. But there are so many! Which one are you looking forward the most to, and what excites you about it?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Why you should NOT use Jest w/ Solid</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Wed, 06 Mar 2024 07:18:13 +0000</pubDate>
      <link>https://dev.to/daelmaak/why-you-should-not-use-jest-w-solid-3i00</link>
      <guid>https://dev.to/daelmaak/why-you-should-not-use-jest-w-solid-3i00</guid>
      <description>&lt;p&gt;When I was wondering how to introduce testing to my small Solid app which is not that small anymore, hence the need for tests, and also which testing framework to use, I mostly bumped into Jest. &lt;a href="https://www.solidjs.com/guides/testing" rel="noopener noreferrer"&gt;Solid's official guide&lt;/a&gt; recommends it too. We use it in bigger projects with NX and are somewhat content with it, so I thought, why not?&lt;/p&gt;

&lt;h2&gt;
  
  
  Jest
&lt;/h2&gt;

&lt;p&gt;The official guide suggests both Jest and &lt;a href="https://github.com/lukeed/uvu" rel="noopener noreferrer"&gt;Uvu&lt;/a&gt;. I didn't consider the latter at all since the maintenance has been &lt;a href="https://github.com/lukeed/uvu/graphs/code-frequency" rel="noopener noreferrer"&gt;sporadic&lt;/a&gt; at best since 2020, so Jest was a clear choice. I followed their advice to&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;npx degit solidjs/templates/ts-jest my-solid-project
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and copy the generated configs into my existing project.&lt;/p&gt;

&lt;p&gt;The first thing I noticed was that &lt;strong&gt;Babel&lt;/strong&gt; was used. I have nothing against Babel at all, but I don't like when the way testing code is processed differs from the "production" setup, because it creates room for differences and mistakes. Indeed, they even mention it in the guide that the tests won't be typechecked in this setup, only transpiled.&lt;/p&gt;

&lt;p&gt;Secondly, I was &lt;em&gt;shocked&lt;/em&gt; by the sheer number of dependencies. See for yourself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/core"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.7"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.8"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@babel/preset-typescript"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^7.23.3"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@solidjs/testing-library"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8.5"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@testing-library/jest-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^6.2.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^29.5.11"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"@types/testing-library__jest-dom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^5.14.9"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"babel-jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^29.7.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"babel-preset-jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^29.6.3"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"babel-preset-solid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.8.9"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^29.7.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jest-environment-jsdom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^29.7.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jsdom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^23.2.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"solid-jest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.2.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"solid-testing-library"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.5.1"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is bad on so many levels. First of all, having so many dependencies is a recipe for problems when so many moving parts have to stay in sync. Secondly, I wasn't even sure what at least half of them was for! Frankly, it's a mess.&lt;/p&gt;

&lt;p&gt;Indeed, I got to appreciate all this very soon because running &lt;code&gt;pnpm test&lt;/code&gt; produced &lt;code&gt;SyntaxError: Unexpected token 'export'&lt;/code&gt;. To keep this short, I can tell you I tried all the suggestions Jest gives on this topic but nothing worked. Jest claims to have &lt;a href="https://jestjs.io/docs/ecmascript-modules" rel="noopener noreferrer"&gt;experimental support&lt;/a&gt; for ES6 modules, but it's not exactly easy to use it, and it didn't help. And again, this could have been so easy, given my production setup already worked, only if Jest could use it without introducing its own.&lt;/p&gt;

&lt;p&gt;After trying to get it work for an hour, I decided to give up on Jest and the official testing guide and try something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vitest
&lt;/h2&gt;

&lt;p&gt;I am sure you knew where this is going. &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt; is a Vite-native test runner, meaning if you already are using Vite, you can use the same pipeline for your production and testing code. Solid's starter template even uses Vite by default, so it fits in naturally. &lt;/p&gt;

&lt;p&gt;Solid even has a &lt;a href="https://github.com/solidjs/templates/tree/main/ts-vitest" rel="noopener noreferrer"&gt;template&lt;/a&gt; for this exact setup, but I decided to keep things absolutely simple, and followed Vitest's &lt;a href="https://vitest.dev/guide/" rel="noopener noreferrer"&gt;installation guide&lt;/a&gt;. This boiled down to installing these dependencies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"@solidjs/testing-library"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^0.8.6"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"jsdom"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^24.0.0"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"vitest"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^1.3.1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Solid's testing-library for utilities, &lt;code&gt;jsdom&lt;/code&gt; to be able to mount components, &lt;code&gt;vitest&lt;/code&gt; as the testing engine. And to put cherry on top, I didn't have to change &lt;em&gt;any&lt;/em&gt; configuration. Beautiful!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Just use Vitest! Jest did a lot for us in the past years, but it can be a real pain to set up and packs too many dependencies.&lt;/li&gt;
&lt;li&gt;Vitest should be the preferred testing solution in Solid's docs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;PS. I'll go down to Solid's Discord and suggest the change in the docs. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>solidjs</category>
      <category>jest</category>
      <category>vitest</category>
    </item>
    <item>
      <title>When NOT to use A/B tests</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Mon, 05 Feb 2024 07:32:50 +0000</pubDate>
      <link>https://dev.to/daelmaak/when-not-to-use-ab-tests-1ag7</link>
      <guid>https://dev.to/daelmaak/when-not-to-use-ab-tests-1ag7</guid>
      <description>&lt;p&gt;A/B testing is de facto the standard for determining which experience (treatment or control) is better. If you can, I definitely recommend running them, as it makes your launch/no-launch decision grounded. But even with enough users and proper setup, there are cases where A/B testing is not the answer. Explore with me why this might be the case and what are the alternatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  When A/B is not the best idea
&lt;/h2&gt;

&lt;p&gt;Sometimes it's better to use a different experimental method, other times an experiment is not needed or even possible at all. Let's see the cases in which a developer like you and me can question the need of A/B.&lt;/p&gt;

&lt;h3&gt;
  
  
  Not enough units
&lt;/h3&gt;

&lt;p&gt;As I have highlighted in my &lt;a href="https://dev.to/daelmaak/why-do-ab-testing-3fdm"&gt;previous article&lt;/a&gt;, one doesn't need that many users to run A/B tests. However, it might happen the circumstances lower the number of experiment randomization units (usually users) so much that A/B test can't be run. &lt;/p&gt;

&lt;p&gt;Picture that you have enough users, but a certain experiment requires that you randomize by &lt;em&gt;accounts&lt;/em&gt; instead, say because the experience must remain consistent between account members. Depending on the average size of the accounts, this can dramatically reduce number of units on which you can experiment, and it could take too long to gather any significant results.&lt;/p&gt;

&lt;h3&gt;
  
  
  Just launch already!
&lt;/h3&gt;

&lt;p&gt;Here we are talking a situation where it's clear that the treatment experience is superior.&lt;/p&gt;

&lt;p&gt;Imagine you have an online product which users can procure on your payment site. The product is great but the payment site not so much because when the payment fails, it gives the user no explanation why. This is frustrating since users don't know what to do next. Call the bank, recheck their billing details, or something else? &lt;/p&gt;

&lt;p&gt;Now let's say you decided to solve this problem by displaying a nice dialog with an useful explanatory message and your first instinct is to A/B test it. You might think that users hate dialogs, therefore you would like to be sure it performs well. Or you just got into the habit of A/B testing everything and you love the dopamine rush when you've improved key metrics. No worries, it's addictive alright.&lt;/p&gt;

&lt;p&gt;But the thing you might not realize is that while running the experiment, say for 2 weeks, the control group is exposed to objectively worse experience. Not only are your users frustrated but your business is losing them every day. While a dialog might not be the best UI widget to communicate payment failure, it's miles better than having no failure communication at all. In other words, you make the UX worse only to receive test results that are clearly expected. And since the launch/no-launch decision &lt;strong&gt;is clear upfront&lt;/strong&gt;, and &lt;strong&gt;there is no learning to be made&lt;/strong&gt;, running an A/B test is a waste here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Large UI redesigns
&lt;/h3&gt;

&lt;p&gt;Unless such redesigns are approached in step-wise, widget-by-widget fashion, big bang introduction of a new design probably doesn't make sense to be A/B tested. &lt;/p&gt;

&lt;h4&gt;
  
  
  Design phase
&lt;/h4&gt;

&lt;p&gt;That doesn't mean you shouldn't keep control in other ways. First of all, you should have a clear goal in mind. Is it just a design refresh, or a larger overhaul of the UX to reduce friction? And is redesign needed at all? User Research will help you clear up these questions. &lt;/p&gt;

&lt;p&gt;Whichever way you decide to go, it's always a good idea to start testing your designs asap to uncover problems and frictions, usually by using qualitative methods. So far, no quantitative experimenting is generally needed, unless you'd like to target specific areas of the UI and compare pre and post redesign components. This is not always possible as introducing newly designed components might be perceived as disturbing in context of the old design, but can otherwise be great at providing early learning and confidence in the redesign's direction.&lt;/p&gt;

&lt;h4&gt;
  
  
  Roll out phase
&lt;/h4&gt;

&lt;p&gt;Generally it makes sense to gradually roll it out by traffic % and segments, eg. first to beta users, afterwards to new users only. So now is the time to launch the A/B test right? Not really, consider this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The treatment UI can be so different from the old that you would be comparing apples to oranges&lt;/li&gt;
&lt;li&gt;For that reason, your findings might not be actionable. If a certain widget underperforms, is it due to the widget itself, its surroundings or something user has/hasn't seen before?&lt;/li&gt;
&lt;li&gt;If treatment underperforms as a whole, what do you do? Revert the treatment roll out until you fix it? How are users going to react to their UI switching from new to old and back? And isn't it too late for such findings anyway?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That brings us to an important point - A/B tests actually align with the &lt;a href="https://agilemanifesto.org/principles.html" rel="noopener noreferrer"&gt;Agile philosophy&lt;/a&gt;. They should make it &lt;strong&gt;easy&lt;/strong&gt;, &lt;strong&gt;cheap&lt;/strong&gt; and &lt;strong&gt;quick&lt;/strong&gt; to test ideas and throw them away if they don't perform. A/Bing whole new UI after months of work isn't cheap nor quick, and lot of times the interested parties know that throwing away the new shiny UI is just not an option either.&lt;/p&gt;

&lt;p&gt;Therefore, don't allow it come this far without knowing your redesign is a good idea. Validate it and test your concepts and designs early to keep the cost down should your approach be wrong. Once you start rolling out, by all means track both old and new UI and even compare them to eg. discover seasonality effects on your metrics.&lt;/p&gt;

&lt;h2&gt;
  
  
  If no A/B, what then?
&lt;/h2&gt;

&lt;p&gt;Let me start by saying that even though A/B might not be possible in certain cases, you still have to retain control. It goes without saying that you should have solid monitoring in place in order to detect failures of all kinds. But equally important, you need a comprehensive tracking to keep an eye on the important metrics. If the number of purchases drops below acceptable threshold, you need to know about it asap in order to properly react.&lt;/p&gt;

&lt;p&gt;But besides that, how do you make that launch/no-launch decision without an A/B test?&lt;/p&gt;

&lt;h3&gt;
  
  
  Interrupted time series (ITS)
&lt;/h3&gt;

&lt;p&gt;Enter ITS. It is a quasi experimental design, one of most more reliable and universal to use in this class. I say &lt;em&gt;quasi&lt;/em&gt; because control and treatment can't be randomized properly with it. &lt;/p&gt;

&lt;p&gt;It works by measuring period before intervention (control), projecting it into the future (counterfactual) and comparing it with measurement after the intervention (treatment). The difference between the projection and actual treat measurements is the outcome of this method. The intervention is performed for all your users, that means control and treatment never exist at the same time.&lt;/p&gt;

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

&lt;p&gt;The forecast should be done using an ML model to get the best precision. &lt;/p&gt;

&lt;p&gt;Using ITS, you can work around cases where A/B is not possible or desirable and still compare control and treatment, yey! However, as most things in life, ITS is not a silver bullet, far from it actually. All quasi experimental designs have a common weakness - it's very easy to make a mistake and completely invalidate the results. In fact, it's not uncommon for quasi experimental methods to get their results completely refuted by a proper experimental designs later on. &lt;/p&gt;

&lt;p&gt;In the case of ITS, you have to account for confounding effects, seasonality being the most obvious. To mitigate, the interventions can be toggled multiple times to explore seasonality's impact on the results. Still, there can be other confounds at play, and as such ITS requires great care when designing to discover those early on.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Long term holdouts
&lt;/h3&gt;

&lt;p&gt;This term describes a situation where the new experience is rolled out to most people, typically around 90%, and the rest is left with the old experience for a longer time. &lt;/p&gt;

&lt;p&gt;As you have probably guessed, this is in no way an experiment approach, but rather a verification step. It is usually used to spot novelty or other long-term effects (and as such can be used after an A/B test), but in my opinion it can be also used to give a peace of mind when rolling out treatment experience to almost all users without prior verification in an A/B test. An example could be that if you launched to 100%, and suddenly met with a dip in important metrics, you might be unsure if the treatment experience or other factors like seasonality are to blame. In contrast, having a small long term hold out might clear this question up.&lt;/p&gt;

&lt;p&gt;However, it's important to say that withholding treatment from the holdout users, knowing it is superior, can be unethical, so use it wisely.&lt;/p&gt;

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

&lt;p&gt;There are many cases where A/B tests are not possible or desirable. While it's definitely possible to keep control over launching new experiences without them, they are still superior to alternative methods of conducting experiments and should be preferred where possible.&lt;/p&gt;

&lt;p&gt;Let me know what you think and I'll see you in the next one ❤️.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;p&gt;Kohavi R, Tang D, Xu Y. Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing. Cambridge University Press; 2020.&lt;br&gt;
&lt;a href="https://towardsdatascience.com/sample-size-planning-for-interrupted-time-series-design-in-health-care-e16d22bba13f" rel="noopener noreferrer"&gt;https://towardsdatascience.com/sample-size-planning-for-interrupted-time-series-design-in-health-care-e16d22bba13f&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>learning</category>
    </item>
    <item>
      <title>SolidJS: Purpose of the `keyed` attribute in &lt;Show&gt;</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Tue, 02 Jan 2024 14:44:08 +0000</pubDate>
      <link>https://dev.to/daelmaak/solidjs-purpose-of-the-keyed-attribute-in-33bg</link>
      <guid>https://dev.to/daelmaak/solidjs-purpose-of-the-keyed-attribute-in-33bg</guid>
      <description>&lt;p&gt;If you are like me, you are thoroughly confused by the &lt;code&gt;keyed&lt;/code&gt; attribute in the &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt; control flow component from Solid's core library. Well not anymore since I'll explain everything. Let's talk why you should care and how to use it properly.&lt;/p&gt;

&lt;p&gt;I first met with &lt;code&gt;keyed&lt;/code&gt; when I wanted to use &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt; in a more convenient way. So typically one uses Show like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Show&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And indeed if that's all you need, you don't have to care. The problem is that, if you are using any static type checking like TypeScript, the &lt;code&gt;currentUser()&lt;/code&gt; part can at least in theory return falsy value. So if you plan to use it inside &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt;, you'd have to check for truthiness all the time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Show&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is inefficient and tedious, but even worse it's logically unnecessary because you already know inside the &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt; that the &lt;code&gt;currentUser()&lt;/code&gt; exists, otherwise it wouldn't get displayed. But since the type system doesn't have the guarantee that each call to &lt;code&gt;currentUser()&lt;/code&gt; returns the same value, ie. it can be impure, it has to assume that subsequent calls to &lt;code&gt;currentUser()&lt;/code&gt; can still return falsy value. And calling it &lt;em&gt;inside&lt;/em&gt; &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt; JSX tag doesn't change that fact in any way, because &lt;code&gt;&amp;lt;span&amp;gt;{currentUser()?.name}&amp;lt;/span&amp;gt;&lt;/code&gt; is not scoped to &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt;, but rather to the whole component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enter the &lt;code&gt;keyed&lt;/code&gt; attribute
&lt;/h2&gt;

&lt;p&gt;There is actually great solution and that's using so called type narrowing render function, which looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;age&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Show&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It narrows the type of &lt;code&gt;user&lt;/code&gt; to truthy value since it already knows it's truthy by showing Show's children. Perfect!&lt;/p&gt;

&lt;p&gt;But wait a second, how's this connected to the &lt;code&gt;keyed&lt;/code&gt; attribute? Well before Solid 1.7.0, it &lt;a href="https://github.com/solidjs/solid/blob/v1.6.10/packages/solid/src/render/flow.ts#L86" rel="noopener noreferrer"&gt;wasn't possible&lt;/a&gt; to use this render function without using &lt;code&gt;keyed&lt;/code&gt; as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="nx"&gt;keyed&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This, and the fact the docs don't explain &lt;code&gt;keyed&lt;/code&gt; led to developers thinking this attribute is just about being able to use the render function, but that's wrong! Starting with Solid 1.7.0 the render function can be used also without &lt;code&gt;keyed&lt;/code&gt;, but what's its purpose then?&lt;/p&gt;

&lt;h2&gt;
  
  
  Purpose of &lt;code&gt;keyed&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;If you have experience with React, &lt;code&gt;keyed&lt;/code&gt; is somewhat similar to &lt;a href="https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key" rel="noopener noreferrer"&gt;React &lt;code&gt;key&lt;/code&gt;&lt;/a&gt;. React uses it to keep track of DOM elements so that it can efficiently reuse them instead of destroying and recreating. &lt;/p&gt;

&lt;p&gt;And so does Solid. If you don't use &lt;code&gt;keyed&lt;/code&gt;, when you change &lt;code&gt;currentUser&lt;/code&gt; to someone else, Solid expects the Show's content not to have &lt;em&gt;radically&lt;/em&gt; changed and it reuses the already existing component references and their respective DOM nodes instead of destroying and recreating them, but it of course updates the content based on the changed reactive properties.&lt;/p&gt;

&lt;p&gt;However if you use &lt;code&gt;keyed&lt;/code&gt; and the value in &lt;code&gt;when&lt;/code&gt; attribute has changed, Solid will actually recreate the Show's children from scratch. You can see it in this example:&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://stackblitz.com/edit/solidjs-templates-v98kxw?ctl=1&amp;amp;embed=1&amp;amp;file=src%2FApp.tsx" width="100%" height="500"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Every time you click 'Change current user', currentUser's reference changes, which causes Show's children to be recreated and &lt;code&gt;&amp;lt;UserDetails&amp;gt;&lt;/code&gt; prints 'mounted' to the console. But if you remove &lt;code&gt;keyed&lt;/code&gt; from Show (don't forget to also rewrite &lt;code&gt;user()&lt;/code&gt; to just &lt;code&gt;user&lt;/code&gt; inside Show), it doesn't get mounted more than once.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usage of &lt;code&gt;keyed&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;I'd say you don't want to use &lt;code&gt;keyed&lt;/code&gt; in most cases. First of all you might lose the benefit of type narrowing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()?.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;keyed&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;span&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/span&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;when&lt;/code&gt; now uses the &lt;code&gt;id&lt;/code&gt;, not just the object reference. That's because in practice the &lt;code&gt;currentUser()&lt;/code&gt; reference can change even though the user properties used inside &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt; can stay the same. As a result, the render function now accepts just the &lt;code&gt;id&lt;/code&gt; parameter and not the whole user, which is not necessarily very helpful.&lt;/p&gt;

&lt;p&gt;Secondly it can lead to unnecessary re-renders of &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt;'s content, typically when you use object references in &lt;code&gt;when&lt;/code&gt; attribute. This can lead to undesired side effects. If your component performs initialization like fetching and it gets created from scratch every time the Show's &lt;code&gt;when&lt;/code&gt; attribute changes, it can get pretty inefficient.&lt;/p&gt;

&lt;p&gt;So much for dissuading you from using it. However, &lt;code&gt;keyed&lt;/code&gt; can be still very helpful. If &lt;code&gt;&amp;lt;Show&amp;gt;&lt;/code&gt;'s content needs to be completely recreated, &lt;code&gt;keyed&lt;/code&gt; is a neat way to do so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;currentUser&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Show&lt;/span&gt; &lt;span class="nx"&gt;when&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;keyed&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;StatefulComponentXY&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it's not simple to accommodate &lt;code&gt;StatefulComponentXY&lt;/code&gt; to a new user due to its state or the fact that the component shouldn't be visible with the previous user shown (the old user might be cached in a resource, waiting for refetch in the meantime), &lt;code&gt;keyed&lt;/code&gt; is a simple way to ensure that. For this purpose, I use this perhaps strange pattern with &lt;code&gt;&amp;lt;Show keyed&amp;gt;&lt;/code&gt; only checking the identity, not reference, of the principal object and when it changes, it recreates its content entirely.&lt;/p&gt;

</description>
      <category>solidjs</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Why do A/B testing?</title>
      <dc:creator>Daniel Macák</dc:creator>
      <pubDate>Mon, 01 Jan 2024 16:16:47 +0000</pubDate>
      <link>https://dev.to/daelmaak/why-do-ab-testing-3fdm</link>
      <guid>https://dev.to/daelmaak/why-do-ab-testing-3fdm</guid>
      <description>&lt;p&gt;I bet you have heard about A/B testing before, but are you actually using it? If not, maybe because it doesn't fit your project, but for a great number of developers controlled experiments (experiments which run A/B tests) are definitely relevant. In this piece I'd like to go through some great reasons to try them out!&lt;/p&gt;

&lt;h2&gt;
  
  
  We don't have enough users!
&lt;/h2&gt;

&lt;p&gt;First of all, let me debunk the myth that controlled experiments are suitable only for tech giants, on the contrary! &lt;/p&gt;

&lt;p&gt;The key point to understand is that these big companies are looking for small, incremental conversion rate (CR) improvements somewhere between +&lt;strong&gt;0.1-2%&lt;/strong&gt;. But if you are a startup and you want to get that additional funding, you are looking for rapid, big improvements on the scale of &lt;strong&gt;10%&lt;/strong&gt; and (much) more which require proportionately less users to verify in an A/B test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; You have CR rate 20%. To detect an absolute 0.1% improvement, you'd &lt;a href="https://www.evanmiller.org/ab-testing/sample-size.html" rel="noopener noreferrer"&gt;need&lt;/a&gt; 2.500.000 users. But to see a 5% (25% relative) you'd need only 1.000 users over the course of a few weeks! That's absolutely doable even for small startups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Improve decision culture
&lt;/h2&gt;

&lt;p&gt;With that out of the way, let's talk decision culture. Unfortunately, in many companies a (no)launch decision is being made based purely on opinion without any backing in the data. This of course can lead to launching features that worsen UX.&lt;/p&gt;

&lt;p&gt;Using A/B tests to validate new features shifts the decision culture from opinionated to proof-based, which brings confidence into improving the product. Moreover it ends unproductive arguments in the team by providing evidence of which features work the best.&lt;/p&gt;

&lt;p&gt;Lastly, A/B test results are often interesting for the stakeholders and increase management's trust in feature team's decisions since they are backed by data. &lt;/p&gt;

&lt;h2&gt;
  
  
  Learn
&lt;/h2&gt;

&lt;p&gt;Learning from your experiments &lt;strong&gt;is at least as important as making that (no)launch decision&lt;/strong&gt;. Each experiment tests a certain hypothesis and the outcome should say in detail whether it was correct and why. Over time, you'll be able to accumulate enough learnings to have an intuition of what works and what doesn't, which makes developing new features more effective.&lt;/p&gt;

&lt;p&gt;Also, should someone else have the same initiative in mind that you already performed a while back, going back to those learnings will steer her in the right direction.&lt;/p&gt;

&lt;p&gt;I also think that articulating the hypothesis and looking for the learning during the experiment helps the team understand better how their initiative ties to the company's strategy, which helps teams focus and work on things that really matter. This fact is incredibly important for companies big and small.&lt;/p&gt;

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

&lt;p&gt;A/B testing is an important tool and a de facto standard when validating product's features. Using it will help you make better product decisions and improve communication inside and outside your team.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;p&gt;Kohavi R, Tang D, Xu Y. Trustworthy Online Controlled Experiments: A Practical Guide to A/B Testing. Cambridge University Press; 2020.&lt;br&gt;
&lt;a href="https://blog.statsig.com/you-dont-need-large-sample-sizes-to-run-a-b-tests-6044823e9992" rel="noopener noreferrer"&gt;https://blog.statsig.com/you-dont-need-large-sample-sizes-to-run-a-b-tests-6044823e9992&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.growthbook.io/" rel="noopener noreferrer"&gt;https://www.growthbook.io/&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.evanmiller.org/ab-testing/sample-size.html" rel="noopener noreferrer"&gt;https://www.evanmiller.org/ab-testing/sample-size.html&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>learning</category>
    </item>
  </channel>
</rss>
