DEV Community: Codux The latest articles on DEV Community by Codux (@codux). https://dev.to/codux 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%2Forganization%2Fprofile_image%2F6366%2Fb4306326-2c48-489e-baf1-638b1647ff31.png DEV Community: Codux https://dev.to/codux en React: Lessons from the Trenches - useEffect x Infinity Peter Shershov Thu, 23 Mar 2023 13:21:46 +0000 https://dev.to/codux/react-lessons-from-the-trenches-useeffect-x-infinity-1e3d https://dev.to/codux/react-lessons-from-the-trenches-useeffect-x-infinity-1e3d <p>Hey! My name is Peter and I’m a software team-lead, working on <a href="proxy.php?url=https://codux.com/?utm_source=peterArticle1&amp;utm_medium=Top" rel="noopener noreferrer">Codux</a> by Wix.com.</p> <p>I’ve been building user interfaces with React for more than 6 years and have made tons of mistakes during this time. From memoizing almost everything in my component to drilling props like I’m looking for oil — I fell into every trap. I’ll try to break down a couple of issues I’ve dealt with and hopefully, it’ll help you jump over pitfalls in the future. Some articles in this series will address bad practices and design concerns while others focus on specific bugs in special use cases.</p> <p>Let’s start with a prevalent one, updating a state within <code>useEffect</code> can cause an infinite loop by re-rendering the component to infinity.</p> <p><a href="proxy.php?url=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%2Fgb5unv6pd3hf6e5wu986.gif" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fgb5unv6pd3hf6e5wu986.gif" alt=" raw `Maximum update depth exceeded` endraw warning"></a></p> <p>What we see above is a warning (“Maximum update depth exceeded”) indicating that there’s an operation occurring too many times, one after the other.</p> <p>This can happen in several scenarios.</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="p">[</span><span class="nx">superheroes</span><span class="p">,</span> <span class="nx">setSuperheroes</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">([]);</span> <span class="kd">const</span> <span class="nx">updateSuperheroes</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">newSuperhero</span> <span class="o">=</span> <span class="nf">generateSuperhero</span><span class="p">();</span> <span class="nf">setSuperheroes</span><span class="p">([...</span><span class="nx">superheroes</span><span class="p">,</span> <span class="nx">newSuperhero</span><span class="p">]);</span> <span class="p">};</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nf">updateSuperheroes</span><span class="p">();</span> <span class="p">},</span> <span class="p">[</span><span class="nx">updateSuperheroes</span><span class="p">]);</span> </code></pre> </div> <p>Consider the example above.<br> First of all, we know that every state update triggers a re-render, meaning that the code written inside the component function gets executed. We also know that <code>useEffect</code> executes when one of two conditions is met after a render:</p> <ol> <li><p>A component is mounted</p></li> <li><p>One of the dependencies (declared in <code>useEffect</code>’s dependency array) has changed.</p></li> </ol> <blockquote> <p>Keep in mind that <code>useEffect</code>’s dependency array should reflect the usage. Omitting a dependency that is used inside the <code>useEffect</code> can cause stale data issues, while including a dependency that does not impact the effect can cause unnecessary re-renders.</p> </blockquote> <p>With these in mind, we can start debugging the code above.<br> We can see that <code>updateSuperheroes()</code> is executed inside a <code>useEffect</code>, which requires us to declare it as a dependency.<br> <code>updateSuperheroes</code> adds superheroes to the state by creating a copy of the <code>superheroes</code> array and adding new entries to it.</p> <p>When the <code>superheroes</code> state gets updated, the component re-renders with the new state, the <code>updateSuperheroes</code> function is created anew which causes <code>useEffect</code> to trigger, restarting this cycle.</p> <h2> What to do? </h2> <p>To prevent something from being created multiple times over multiple renders we often take advantage of memoization. React offers several tools to memoize our functions and values. <code>useCallback</code> is used for functions and <code>useMemo</code> is used for values such as objects or arrays.</p> <blockquote> <p>Primitives that require a minimal computation don't need to be memoized since they are easily comparable, on the other hand, objects, functions, arrays and other complex data structures, could be created anew with every render if declared inside the component.</p> </blockquote> <p>In our case, <code>updateSuperheroes</code> is a function, and should be memoized, therefore, we should leverage <code>useCallback</code>.</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="p">[</span><span class="nx">superheroes</span><span class="p">,</span> <span class="nx">setSuperheroes</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">([]);</span> <span class="kd">const</span> <span class="nx">updateSuperheroes</span> <span class="o">=</span> <span class="nf">useCallback</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">newSuperhero</span> <span class="o">=</span> <span class="nf">generateSuperhero</span><span class="p">();</span> <span class="nf">setSuperheroes</span><span class="p">([...</span><span class="nx">superheroes</span><span class="p">,</span> <span class="nx">newSuperhero</span><span class="p">]);</span> <span class="p">},</span> <span class="p">[</span><span class="nx">superheroes</span><span class="p">]);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nf">updateSuperheroes</span><span class="p">();</span> <span class="p">},</span> <span class="p">[</span><span class="nx">updateSuperheroes</span><span class="p">]);</span> </code></pre> </div> <p>But this is not enough. We can see that <code>useCallback</code> has a dependency on <code>superheroes</code> that gets changed on every run of <code>useEffect</code>, making this memoization redundant. For such cases, React provides us with another way to update the state without being dependent on the value.</p> <p>Every state setter can get two types of the first argument.</p> <p>One, a new state:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="p">[</span><span class="nx">state</span><span class="p">,</span> <span class="nx">setState</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">([])</span> <span class="c1">// ...</span> <span class="nf">setState</span><span class="p">([...</span><span class="nx">state</span><span class="p">,</span> <span class="dl">'</span><span class="s1">something new</span><span class="dl">'</span><span class="p">]);</span> </code></pre> </div> <p>Or two, a function that gets the previous state as an argument and returns a new state.</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="p">[</span><span class="nx">state</span><span class="p">,</span> <span class="nx">setState</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">([])</span> <span class="c1">// ...</span> <span class="nf">setState</span><span class="p">(</span><span class="nx">previousState</span> <span class="o">=&gt;</span> <span class="p">[...</span><span class="nx">previousState</span><span class="p">,</span> <span class="dl">'</span><span class="s1">something new</span><span class="dl">'</span><span class="p">]);</span> </code></pre> </div> <p>Using the latter way in our example will fix the infinite loop since there is no dependency on the state anymore. It will trigger only when the component mounts.</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="p">[</span><span class="nx">superheroes</span><span class="p">,</span> <span class="nx">setSuperheroes</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">([]);</span> <span class="kd">const</span> <span class="nx">updateSuperheroes</span> <span class="o">=</span> <span class="nf">useCallback</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">newSuperhero</span> <span class="o">=</span> <span class="nf">generateSuperhero</span><span class="p">();</span> <span class="nf">setSuperheroes</span><span class="p">((</span><span class="nx">previousSuperheroes</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">[</span> <span class="p">...</span><span class="nx">previousSuperheroes</span><span class="p">,</span> <span class="nx">newSuperhero</span> <span class="p">]);</span> <span class="p">},</span> <span class="p">[]);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nf">updateSuperheroes</span><span class="p">();</span> <span class="p">},</span> <span class="p">[</span><span class="nx">updateSuperheroes</span><span class="p">]);</span> </code></pre> </div> <p>In some cases, you can extract values to an outer scope (outside of the component) when a value is not dependent on any specific component code.</p> <p>For example, instead of:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">emptyObject</span> <span class="o">=</span> <span class="nf">useMemo</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">({},</span> <span class="p">[]);</span> </code></pre> </div> <p>Move <code>emptyObject</code> outside of the component code:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">emptyObject</span> <span class="o">=</span> <span class="p">{}</span> <span class="kd">function</span> <span class="nf">Superheroes</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">}</span> </code></pre> </div> <p>This will prevent the creation of a new object instance for every render without the need to memoize it over time.</p> <h2> What can be so complex? </h2> <p>Now that we know how to handle the simple case of a state update within a <code>useEffect</code>, let’s delve into the more complex scenarios.</p> <p>In the example above we could very easily tell which dependency in our <code>useEffect</code> is causing the infinite loop. Debugging more complex cases where <code>useEffect</code> has a large number of dependencies (and each one of them might have its own set of dependencies) is a bit harder. Finding the culprit and maintaining this code in general requires more effort.</p> <p>I’ve seen colossal dependency arrays across many projects, and here are a couple of recommendations for how to deal with them.</p> <h3> 1. Separate your gigantic <code>useEffect</code> into a few smaller ones </h3> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">},</span> <span class="p">[</span> <span class="nx">peopleProvider</span><span class="p">,</span> <span class="nx">engineersProvider</span><span class="p">,</span> <span class="nx">doctorsProvider</span><span class="p">,</span> <span class="nx">generateUniqueId</span><span class="p">,</span> <span class="nx">initializeApp</span> <span class="p">]);</span> </code></pre> </div> <p>Converting this example, where a <code>useEffect</code> gets a large number of possibly non-related dependencies, into this:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">},</span> <span class="p">[</span><span class="nx">peopleProvider</span><span class="p">]);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">},</span> <span class="p">[</span><span class="nx">engineersProvider</span><span class="p">]);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">},</span> <span class="p">[</span><span class="nx">doctorsProvider</span><span class="p">]);</span> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">},</span> <span class="p">[</span><span class="nx">generateUniqueId</span><span class="p">,</span> <span class="nx">initializeApp</span><span class="p">]);</span> </code></pre> </div> <p>This way we can scope our side effects into separate, smaller <code>useEffect</code> declarations to prevent triggering irrelevant handlers.<br> In other words, updated dependencies will not trigger a side effect or a state update for a non-relevant functionality.</p> <h3> 2. Debug the dependencies that are changing on every render </h3> <p>If you still couldn’t find the main cause of an infinite render and you still don’t know which state gets updated and what handler is causing it, I recommend implementing or using a helper hook (<a href="proxy.php?url=https://github.com/damiangreen/use-trace-update" rel="noopener noreferrer">such as this</a>) that logs the changed dependencies for each render.</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="nf">useEffect</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="c1">// ...</span> <span class="p">},</span> <span class="p">[</span> <span class="nx">peopleProvider</span><span class="p">,</span> <span class="nx">engineersProvider</span><span class="p">,</span> <span class="nx">doctorsProvider</span><span class="p">,</span> <span class="nx">generateUniqueId</span><span class="p">,</span> <span class="nx">initializeApp</span> <span class="p">]);</span> <span class="nf">useTraceUpdate</span><span class="p">({</span> <span class="nx">peopleProvider</span><span class="p">,</span> <span class="nx">engineersProvider</span><span class="p">,</span> <span class="nx">doctorsProvider</span><span class="p">,</span> <span class="nx">generateUniqueId</span><span class="p">,</span> <span class="nx">initializeApp</span> <span class="p">});</span> </code></pre> </div> <p>This will log only the changed values, indicating which dependency is changing every render leading to an infinite loop.</p> <h2> Conclusion </h2> <p>Fixing and avoiding <code>useEffect</code>’s infinite renders can be achieved by applying the following measures:</p> <ul> <li>Keep your dependency arrays short and concise. Scope the <code>useEffect</code> you’re creating to a specific minimal context and avoid mixing dependencies that have different intents.</li> <li>Memoize complex values and functions passed to your hooks and components.</li> <li>Avoid depending on a state and setting it in the same <code>useEffect</code>, this is a <em>very</em> fragile situation that can break easily. If you must have such a case, you can take advantage of the option to set the state using a function provided to <code>setState</code> and not be dependent on the previous value.</li> <li>Find the always-changing dependency (the one that is causing the infinite render) by debugging your dependencies. Try locating the value that is causing the infinite renders and memoize it correctly.</li> </ul> <p>I hope you've found this article helpful, and I look forward to see y'all at the next one!</p> react webdev javascript frontend Experiments with the JavaScript Garbage Collector Alexey Lebedev Thu, 23 Feb 2023 17:39:47 +0000 https://dev.to/codux/experiments-with-the-javascript-garbage-collector-2ae3 https://dev.to/codux/experiments-with-the-javascript-garbage-collector-2ae3 <p>Memory leaks in web applications are widespread and notoriously difficult to debug. If we want to avoid them, it helps to understand how the garbage collector decides what objects can and cannot be collected. In this article we'll take a look at a few scenarios where its behavior might surprise you.</p> <p>If you're unfamiliar with the basics of garbage collection, a good starting point would be <a href="proxy.php?url=https://hacks.mozilla.org/2017/06/a-crash-course-in-memory-management/" rel="noopener noreferrer">A Crash Course in Memory Management</a> by Lin Clark or <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management" rel="noopener noreferrer">Memory Management</a> on MDN. Consider reading one of those before continuing.</p> <h2> Detecting Object Disposal </h2> <p>Recently I've learned that JavaScript provides a class called <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry" rel="noopener noreferrer">FinalizationRegistry</a> that allows you to programmatically detect when an object is garbage-collected. It's available in all major web browsers and Node.js.</p> <p>A basic usage example:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> <span class="c1">// Some time later: "x has been collected"</span> </code></pre> </div> <p>When the <code>example()</code> function returns, the object referenced by <code>x</code> is no longer reachable and can be disposed of.</p> <p>Most likely, though, it won't be disposed immediately. The engine can decide to handle more important tasks first, or to wait for more objects to become unreachable and then dispose of them in bulk. But you can force garbage collection by clicking the little trash icon in the DevTools ➵ Memory tab. Node.js doesn't have a trash icon, but it provides a global <code>gc()</code> function when launched with the <code>--expose-gc</code> flag.</p> <p><a href="proxy.php?url=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%2Fl23os4ss0asqatwrmvq2.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fl23os4ss0asqatwrmvq2.png" alt="DevTools Memory Tab"></a></p> <p>With <code>FinalizationRegistry</code> in my bag of tools, I decided to examine a few scenarios where I wasn't sure how the garbage collector was going to behave. I encourage you to look at the examples below and make your own predictions about how they're going to behave. </p> <h2> Example 1: Nested Objects </h2> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{};</span> <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="p">{};</span> <span class="kd">const</span> <span class="nx">z</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="p">};</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">y</span><span class="p">,</span> <span class="dl">'</span><span class="s1">y has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">z</span><span class="p">,</span> <span class="dl">'</span><span class="s1">z has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">globalThis</span><span class="p">.</span><span class="nx">temp</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> </code></pre> </div> <p>Here, even though the variable <code>x</code> no longer exists after the <code>example()</code> function has returned, the object referenced by <code>x</code> is still being held by the <code>globalThis.temp</code> variable. <code>z</code> and <code>y</code> on the other hand can no longer be reached from the global object or the execution stack, and will be collected. If we now run <code>globalThis.temp = undefined</code>, the object previously known as <code>x</code> will be collected as well. No surprises here.</p> <h2> Example 2: Closures </h2> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{};</span> <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="p">{};</span> <span class="kd">const</span> <span class="nx">z</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="p">};</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">y</span><span class="p">,</span> <span class="dl">'</span><span class="s1">y has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">z</span><span class="p">,</span> <span class="dl">'</span><span class="s1">z has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">globalThis</span><span class="p">.</span><span class="nx">temp</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="nx">z</span><span class="p">.</span><span class="nx">x</span><span class="p">;</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> </code></pre> </div> <p>In this example we can still reach <code>x</code> by calling <code>globalThis.temp()</code>. We can no longer reach <code>z</code> or <code>y</code>. But what's this, despite no longer being reachable, <code>z</code> and <code>y</code> are not getting collected.</p> <p>A possible theory is that since <code>z.x</code> is a property lookup, the engine doesn't really know if it can replace the lookup with a direct reference to <code>x</code>. For example, what if <code>x</code> is a getter. So the engine is forced to keep the reference to <code>z</code>, and consequently to <code>y</code>. To test this theory, let's modify the example: <code>globalThis.temp = () =&gt; { z; };</code>. Now there's clearly no way to reach <code>z</code>, but it's still not getting collected.</p> <p>What I think is happening is that the garbage collector only pays attention to the fact that <code>z</code> <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures#lexical_scoping" rel="noopener noreferrer"><strong>is in the lexical scope</strong></a> of the closure assigned to <code>temp</code>, and doesn't look any further than that. Traversing the entire object graph and marking objects that are still "alive" is a performance-critical operation that needs to be fast. Even though the garbage collector could theoretically figure out that <code>z</code> is not used, that would be expensive. And not particularly useful, since your code doesn't typically contain variables that are just <em>chilling</em> in there.</p> <h2> Example 3: Eval </h2> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">globalThis</span><span class="p">.</span><span class="nx">temp</span> <span class="o">=</span> <span class="p">(</span><span class="nx">string</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="nf">eval</span><span class="p">(</span><span class="nx">string</span><span class="p">);</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> </code></pre> </div> <p>Here we can still reach <code>x</code> from the global scope by calling <code>temp('x')</code>. The engine cannot safely collect any objects within the lexical scope of <code>eval</code>. And it doesn't even try to analyze what arguments the eval receives. Even something innocent like <code>globalThis.temp = () =&gt; eval(1)</code> would prevent garbage collection.</p> <p>What if eval is hiding behind an alias, e.g. <code>globalThis.exec = eval</code>? Or what if it's used without being ever mentioned explicitly? E.g.:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">.</span><span class="nf">constructor</span><span class="p">(</span><span class="dl">'</span><span class="s1">alert(1)</span><span class="dl">'</span><span class="p">)();</span> <span class="c1">// opens an alert box</span> </code></pre> </div> <p>Does it mean that every function call is a suspect, and nothing ever can be safely collected? Fortunately, no. JavaScript makes a distinction between <a href="proxy.php?url=https://2ality.com/2014/01/eval.html" rel="noopener noreferrer">direct and indirect eval</a>. Only when you directly call <code>eval(string)</code> it will execute the code in the current lexical scope. But anything even a tiny bit less direct, such as <code>eval?.(string)</code>, will execute the code in the global scope, and it won't have access to the enclosing function's variables.</p> <h2> Example 4: DOM Elements </h2> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">y</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span> <span class="kd">const</span> <span class="nx">z</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nf">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">div</span><span class="dl">'</span><span class="p">);</span> <span class="nx">z</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="nx">x</span><span class="p">);</span> <span class="nx">z</span><span class="p">.</span><span class="nf">append</span><span class="p">(</span><span class="nx">y</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">y</span><span class="p">,</span> <span class="dl">'</span><span class="s1">y has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">z</span><span class="p">,</span> <span class="dl">'</span><span class="s1">z has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nx">globalThis</span><span class="p">.</span><span class="nx">temp</span> <span class="o">=</span> <span class="nx">x</span><span class="p">;</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> </code></pre> </div> <p>This example is somewhat similar to the first one, but it uses DOM elements instead of plain objects. Unlike plain objects, DOM elements have links to their parents and siblings. You can reach <code>z</code> through <code>temp.parentElement</code>, and <code>y</code> through <code>temp.nextSibling</code>. So all three elements will stay alive.</p> <p>Now if we execute <code>temp.remove()</code>, <code>y</code> and <code>z</code> will be collected because <code>x</code> has been detached from its parent. But <code>x</code> will not be collected because it's still referenced by <code>temp</code>.</p> <h2> Example 5: Promises </h2> <blockquote> <p>Warning: this example is a more complex one, showcasing a scenario involving asynchronous operations and promises. Feel free to skip it, and jump to the summary below.</p> </blockquote> <p>What happens to promises that are never resolved or rejected? Do they keep floating in memory with the entire chain of <code>.then</code>'s attached to them?</p> <p>As a realistic example, here's a common <a href="proxy.php?url=https://reactjs.org/blog/2015/12/16/ismounted-antipattern.html" rel="noopener noreferrer">anti-pattern</a> in React projects:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">function</span> <span class="nf">MyComponent</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">isMounted</span> <span class="o">=</span> <span class="nf">useIsMounted</span><span class="p">();</span> <span class="kd">const</span> <span class="p">[</span><span class="nx">status</span><span class="p">,</span> <span class="nx">setStatus</span><span class="p">]</span> <span class="o">=</span> <span class="nf">useState</span><span class="p">(</span><span class="dl">''</span><span class="p">);</span> <span class="nf">useEffect</span><span class="p">(</span><span class="k">async </span><span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="k">await</span> <span class="nf">asyncOperation</span><span class="p">();</span> <span class="k">if </span><span class="p">(</span><span class="nf">isMounted</span><span class="p">())</span> <span class="p">{</span> <span class="nf">setStatus</span><span class="p">(</span><span class="dl">'</span><span class="s1">Great success</span><span class="dl">'</span><span class="p">);</span> <span class="p">}</span> <span class="p">},</span> <span class="p">[]);</span> <span class="k">return</span> <span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span><span class="p">{</span><span class="nx">status</span><span class="p">}</span><span class="o">&lt;</span><span class="sr">/div&gt;</span><span class="err">; </span><span class="p">}</span> </code></pre> </div> <p>If <code>asyncOperation()</code> never settles, what's going to happen to the effect function? Will it keep waiting for the promise even after the component has unmounted? Will it keep <code>isMounted</code> and <code>setStatus</code> alive?</p> <p>Let's reduce this example to a more basic form that doesn't require React:</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">asyncOperation</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">,</span> <span class="nx">reject</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="cm">/* never settles */</span> <span class="p">});</span> <span class="p">}</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nf">asyncOperation</span><span class="p">().</span><span class="nf">then</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">x</span><span class="p">));</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> </code></pre> </div> <p>Previously we saw that the garbage collector doesn't try to perform any kind of sophisticated analysis, and merely follows pointers from object to object to determine their "liveness". So it might come as a surprise that in this case <code>x</code> is going to be collected!</p> <p>Let's take a look at how this example might look when something is still holding a reference to the Promise <code>resolve</code>. In a real-world scenario this could be <code>setTimeout()</code> or <code>fetch()</code>.</p> <div class="highlight js-code-highlight"> <pre class="highlight javascript"><code> <span class="kd">const</span> <span class="nx">registry</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FinalizationRegistry</span><span class="p">(</span><span class="nx">message</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">message</span><span class="p">));</span> <span class="kd">function</span> <span class="nf">asyncOperation</span><span class="p">()</span> <span class="p">{</span> <span class="k">return</span> <span class="k">new</span> <span class="nc">Promise</span><span class="p">((</span><span class="nx">resolve</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span> <span class="nx">globalThis</span><span class="p">.</span><span class="nx">temp</span> <span class="o">=</span> <span class="nx">resolve</span><span class="p">;</span> <span class="p">});</span> <span class="p">}</span> <span class="kd">function</span> <span class="nf">example</span><span class="p">()</span> <span class="p">{</span> <span class="kd">const</span> <span class="nx">x</span> <span class="o">=</span> <span class="p">{};</span> <span class="nx">registry</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="nx">x</span><span class="p">,</span> <span class="dl">'</span><span class="s1">x has been collected</span><span class="dl">'</span><span class="p">);</span> <span class="nf">asyncOperation</span><span class="p">().</span><span class="nf">then</span><span class="p">(()</span> <span class="o">=&gt;</span> <span class="nx">console</span><span class="p">.</span><span class="nf">log</span><span class="p">(</span><span class="nx">x</span><span class="p">));</span> <span class="p">}</span> <span class="nf">example</span><span class="p">();</span> </code></pre> </div> <p>Here <code>globalThis</code> keeps <code>temp</code> alive, which keeps <code>resolve</code> alive, which keeps <code>.then(...)</code> callback alive, which keeps <code>x</code> alive. As soon as we execute <code>globalThis.temp = undefined</code>, <code>x</code> can be collected. By the way, saving a reference to the promise itself wouldn't prevent <code>x</code> from being collected.</p> <p>Going back to the React example: if something is still holding a reference to the Promise <code>resolve</code>, the effect and everything in its lexical scope will stay alive even after the component has unmounted. It will be collected when the promise settles, or when the garbage collector can no longer trace the path to the <code>resolve</code> and <code>reject</code> of the promise.</p> <h2> In conclusion </h2> <p>In this article we've taken a look at <code>FinalizationRegistry</code> and how it can be used to detect when objects are collected. We also saw that sometimes the garbage collector is unable to reclaim memory even when it would be safe to do so. Which is why it's helpful to be aware of what it can and cannot do.</p> <p>It's worth noting that different JavaScript engines and even different versions of the same engine can have wildly different implementations of a garbage collector, and externally observable differences between those.</p> <p>In fact, the <a href="proxy.php?url=https://tc39.es/ecma262/#sec-managing-memory" rel="noopener noreferrer">ECMAScript specification</a> doesn't even require implementations to <em>have</em> a garbage collector, let alone prescribe a certain behavior.</p> <p>However, all of the examples above were verified to work the same in V8 (Chrome), JavaScriptCore (Safari), and Gecko (Firefox).</p> javascript performance webdev debugging The Future of CSS Vlad Thu, 09 Feb 2023 11:58:44 +0000 https://dev.to/codux/the-future-of-css-33kl https://dev.to/codux/the-future-of-css-33kl <p>I have to admit: I don’t always have time to keep up with all the new CSS technologies, and I don’t always know which ones are worth my time.</p> <p>To organize my thoughts, I produced an overview of all the major CSS technologies that are gaining popularity today and might still be relevant tomorrow: CSS-in-JS, Component-Oriented CSS*, and Utility First CSS.</p> <p>I wrote about the big ideas behind them and the consequences of these ideas for code architecture, performance, and dev velocity.</p> <p>You can try all the code examples via Codux by clicking <a href="proxy.php?url=https://vlad-karnauch.github.io/the-future-of-css/" rel="noopener noreferrer">here</a>.</p> <blockquote> <ul> <li>Component Oriented CSS: A term I coined, it refers to CSS frameworks, mostly preprocessors, that scope CSS locally, such as Stylable, or CSS Modules.</li> </ul> </blockquote> <h2> Vanilla CSS </h2> <p>CSS (Cascading Style Sheets) was initially released in 1996 during the Browser wars: Microsoft's Internet Explorer and Netscape Navigator. Each tried to define the web, which, at the time, was mostly static pages that displayed data.</p> <p>One of the great advantages of CSS was the way styles “cascaded” from a parent to affect all of its children. This worked very well for the relatively small projects that existed in 1996 because it reduced duplication, made CSS easy to understand, and the inheritance easy to trace.</p> <p>However, as projects grow in complexity, cascading becomes a liability; CSS rules often depend on the HTML structure, so every change in either requires changes in both. In addition, a small change in any rule can have unintended consequences on other pages due to global scope.</p> <p>Using preprocessors like <a href="proxy.php?url=https://sass-lang.com/" rel="noopener noreferrer">SASS</a> or <a href="proxy.php?url=https://lesscss.org/" rel="noopener noreferrer">less</a> and naming conventions like <a href="proxy.php?url=http://getbem.com/introduction/" rel="noopener noreferrer">BEM</a> can help organize a team’s code, but modern technologies can achieve better results with relative ease.</p> <h2> CSS-in-JS </h2> <h3> <a href="proxy.php?url=https://styled-components.com/" rel="noopener noreferrer">Styled Components</a> </h3> <div class="ltag_gist-liquid-tag"> </div> <br> Styled Components is a package that is installed using the package manager. <p>Button component is created by providing the HTML tag (button), and the style is provided as a string using a <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates" rel="noopener noreferrer">tagged template string</a>. Inside, Styled-Components creates a unique className for the new Button Component and adds the <code>className</code> to the <a href="proxy.php?url=https://developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model" rel="noopener noreferrer">CSSOM</a>*</p> <p>Finally, <code>styled.button</code> returns a new component that uses the newly created style so that it can be used.</p> <p><a href="proxy.php?url=https://vlad-karnauch.github.io/the-future-of-css/?simulation=%2Fsrc%2F_wcs%2Fboards%2Fstyled-components-button%2Fstyled-components.board.tsx&amp;type=board" rel="noopener noreferrer">Try it out with Codux</a></p> <blockquote> <ul> <li>CSSOM (CSS Object Model) is like the DOM, but for CSS, it allows adding CSS at runtime.</li> </ul> </blockquote> <h3> <a href="proxy.php?url=https://emotion.sh/docs/introduction" rel="noopener noreferrer">Emotion</a> </h3> <div class="ltag_gist-liquid-tag"> </div> <br> Emotion is also a package that can be installed using the package manager. It works similarly to Styled-Components: the CSS prop is replaced with a uniquely generated <code>className</code>, and the style is added to the CSSOM. <p>By now, most libraries that allow CSS-in-js support both syntaxes.</p> <p>The tagged-template-string format is useful because we can directly load legacy CSS from files, but it doesn’t support any syntax highlighting and code completion supported by the CSS prop out of the box.</p> <p><a href="proxy.php?url=https://vlad-karnauch.github.io/the-future-of-css/?simulation=%2Fsrc%2F_wcs%2Fboards%2Femotion-button%2Femotion.board.tsx&amp;type=board" rel="noopener noreferrer">Try it out with Codux</a></p> <h3> CSS-in-JS Advantages </h3> <ul> <li>Local scoping, since a unique className is generated for each component, the styling is now scoped to the component, and styles will no longer override each other simply because they are no longer global. In other words: these components can be extended but not modified from the outside.</li> <li>Global scoping (cascading) was essential for CSS to avoid code duplication, instead sharing is easy because this is javascript code</li> <li>The main building block of our app is now the component. This no longer feels like regular CSS. Instead, it becomes functional programming: the styling of a component is now an integral part of it, and they are both encapsulated into a single unit that exposes an API that can be reused.</li> <li>This is javascript code, making it much easier to apply complex logic to your style rules, create dynamic interfaces, and reuse code.</li> </ul> <h3> CSS-in-JS Disadvantages </h3> <ul> <li>Loss of separation of concerns, CSS was designed so that content would be separated from presentation, with CSS-in-js component logic, animations, themes, and layouts all mixed together in the component; when not carefully managed, this could lead to unmaintainable code.</li> <li>Browsers can render regular CSS much faster than styling that requires code to run; this can be alleviated by separating the critical CSS needed to load the user’s initial viewport and is further helped by server-side rendering.</li> <li>An Extra layer of complexity needs to be set up and maintained and requires a steeper learning curve than vanilla CSS. This may be worth it for teams working on relatively large projects but can be detrimental in smaller ones.</li> </ul> <h3> <a href="proxy.php?url=https://vanilla-extract.style/" rel="noopener noreferrer">Side Note - Vanilla Extract</a> </h3> <div class="ltag_gist-liquid-tag"> </div> <br> This is a newer solution, which I have <em>yet</em> to try out extensively, but, after reading this article, a friend recommended that I also mention Vanilla Extract. <br> Vanilla Extract "uses TypeScript as a preprocessor" so it looks like CSS, but it's written in TypeScript, and so it requires learning a new Syntax. <p>It offers some of the runtime advantages CSS-in-JS has through css variables and classes, but unlike CSS-in-JS it's very fast because it generates atomic static CSS in build-time.<br> Vanilla Extract is still very new, so it doesn't enjoy the popularity some of the other frameworks do, but it is definitely on my watch list.</p> <h2> Component-Oriented CSS </h2> <blockquote> <p>keep everything we liked about CSS, and build upon the good work that the styles-in-JS community was producing. So, while we’re bullish about our approach and firmly defend the virtues of CSS, we owe a debt of gratitude to those folks pushing the boundaries in the other direction. Thanks, friends! 👬👫👭 — css modules team</p> </blockquote> <h3> <a href="proxy.php?url=https://github.com/css-modules/css-modules" rel="noopener noreferrer">CSS Modules</a> </h3> <p>CSS Modules is a pre-processing step: by default, styles are scoped locally to the current component, and the transpiler ensures no conflicts.</p> <p>Let’s see how it works. First, we add a <code>button.css</code>, just like with good old CSS.<br> </p> <div class="ltag_gist-liquid-tag"> </div> <br> Next, we import styles from the CSS we just created and apply <code>className</code>s from it to our JSX:<br> <div class="ltag_gist-liquid-tag"> </div> <br> The transpiler will make these styles locally scoped, and the result looks something like this:<br> <div class="ltag_gist-liquid-tag"> </div> <br> <div class="ltag_gist-liquid-tag"> </div> <h4> Avoiding Code Duplication </h4> <p>By scoping locally, we sidestep the global scope issue but lose some of the advantages gained by cascading. Instead, to avoid code duplication, we use the composes keyword:<br> Let’s say we want a cancel button that is the same but with a different text color:<br> </p> <div class="ltag_gist-liquid-tag"> </div> <br> CSS modules adds both <code>classNames</code> to the component. <h3> <a href="proxy.php?url=https://stylable.io/" rel="noopener noreferrer">Stylable</a> </h3> <p>CSS Modules helps manage large CSS projects by adding namespaces. Stylable is a CSS preprocessor that takes it even further by adding a type system.<br> Stylable exposes an API that can be styled, meaning selector issues are visible in build-time rather than run-time. For example, you’ll see it in the IDE if a button no longer supports an icon. Every developer knows that detecting and fixing a bug costs exponentially more during acceptance testing than during coding.<br> Stylable files look very similar to CSS files, but they are scoped:<br> </p> <div class="ltag_gist-liquid-tag"> </div> <br> In addition, because we now have an elaborate type system, we can now extend this button from parent components; for example, this could be a dialog containing our button:<br> <div class="ltag_gist-liquid-tag"> </div> <br> <a href="proxy.php?url=https://vlad-karnauch.github.io/the-future-of-css/?simulation=%2Fsrc%2F_wcs%2Fboards%2Fstylable-dialog%2Fstylable-dialog.board.tsx&amp;type=board" rel="noopener noreferrer">Try it out with Codux</a> <h3> Component Oriented CSS Advantages </h3> <ul> <li>We can “compose” some of the CSS-in-JS advantages here: local scoping, composition, and the main building block of our project is still the component. That’s why I called it “component oriented.”</li> <li>Concerns are separated, just like with CSS: themes and layouts are, by default, separated from the component without the need to manage them carefully.</li> <li>Runtime performance is not affected, unlike CSS-in-JS, which executes in runtime and potentially slows down your app; this is a preprocessing step, resulting in plain CSS, which the browser is already heavily optimized for.</li> </ul> <h3> Component-Oriented CSS Disadvantages </h3> <ul> <li>No longer dynamic, complex logic is more challenging to implement; some styling features can’t be implemented without JS.</li> <li>An extra layer of complexity, though with a gentler learning curve than what CSS-in-JS offers.</li> </ul> <h2> Utility First CSS </h2> <p>The idea is to have reusable, tiny classes like <code>flex</code>,<code>pt-4</code>, <code>text-center</code>, and <code>rotate-90</code> that can be composed and used in any component.</p> <p>Consequentially, around 90%-95% of most projects’ CSS can be translated into these atoms. For example, the component below has the following HTML code:</p> <p><a href="proxy.php?url=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%2F64x3fjwu04443lmwuqtg.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F64x3fjwu04443lmwuqtg.png" alt="Tailwind Alert"></a><br> </p> <div class="ltag_gist-liquid-tag"> </div> <h3> Utility-First CSS Advantages </h3> <ul> <li>HTML is now dependent on CSS; it has to know that certain CSS classes exist and how to combine them properly, making the CSS completely independent of the HTML and allowing programmers to quickly iterate on the design system regardless of the content. Smooth Iterations mean fast improvements and sleek products; that’s why Utility-First CSS results in some of the best component libraries. Check out <a href="proxy.php?url=https://tailwindcss.com/" rel="noopener noreferrer">Tailwind</a>, and <a href="proxy.php?url=https://getbootstrap.com/" rel="noopener noreferrer">Bootstrap</a> </li> <li>The design system is more consistent, <code>border-dark-soft</code>, will always have the same color and width, and can be easily changed in all places. However, this can easily be achieved using composition and variables.</li> <li>CSS files are much smaller. CSS usually grows linearly with project size: more components mean more classes and larger files, while utility-first CSS grows logarithmically with project size: <code>border-dark-soft</code> is defined once and used many times</li> </ul> <h3> Utility-First CSS Disadvantages </h3> <ul> <li>CSS was designed to be independent of HTML, that’s why we name classes based on content: button, author-bio, etc., or in other words, CSS is dependent on HTML and needs to know what classes were used. At the end of the day, the more volatile parts of our software (that change the most) should depend on the ones that change the least: in most projects, users like features and functionality (e.g., components) to be continually added, but they like the design-system to stay the same.</li> <li>The naming convention requires a learning curve: what is <code>py-2</code>? We end up with these long unreadable classes: <code>py-2 px-4 border-r border-dark-soft</code>, that are hard to learn and difficult to understand.</li> <li>Adds another layer of complexity to our stack, sometimes requiring setting up the naming conventions and adding a post-CSS processor to minify the result.</li> </ul> <h2> At the end of the day… </h2> <p>CSS has a fascinating future ahead of it. These technologies are here to stay, grow, and become easier to use. However, teams must be careful when selecting a specific technology over others. In general:</p> <ul> <li>CSS-in-JS is worth the performance hit when complex logic is associated with styling.</li> <li>Component Oriented CSS is perfect for organizing large projects, with many independent components.</li> <li>Utility-First CSS seems to produce the best-looking interfaces with the least amount of effort, this makes a lot of sense: if the CSS is decoupled from the component structure, it can be iterated upon frequently.</li> </ul> css webdev javascript react Flaky Tests, and How to Deal with Them Yarden Porat Thu, 26 Jan 2023 15:45:35 +0000 https://dev.to/codux/flaky-tests-and-how-to-deal-with-them-2id2 https://dev.to/codux/flaky-tests-and-how-to-deal-with-them-2id2 <p><a href="proxy.php?url=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%2Fnf69kaxw58c98p40udb1.jpg" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fnf69kaxw58c98p40udb1.jpg" alt="Battling Flakiness" width="800" height="534"></a></p> <h2> Intro </h2> <p>Hey! My name is Yarden Porat, and in this article, I will explain what flaky tests are, their costs, their causes, and how they harm your work and organization. Once we have that figured out, I will share our strategy and tools we have developed in-house for dealing with test flakiness at Wix and how we avoid their costs.</p> <h2> What is a flaky test? </h2> <p>A flaky test is an automated test with a non-deterministic result. This is a way of saying that a test sometimes passes and sometimes doesn’t, inconsistently, without any code changes.</p> <p><a href="proxy.php?url=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%2Famfvyjc1e5qrqzxa9yxi.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Famfvyjc1e5qrqzxa9yxi.png" alt="failing test" width="479" height="242"></a></p> <h2> How often does it fail? </h2> <p>Beautifully depicted in this <a href="proxy.php?url=https://shopify.engineering/unreasonable-effectiveness-test-retries-android-monorepo-case-study" rel="noopener noreferrer">article</a>, if a single test has a failure rate of 0.05% (0.0005), and you have 100 of these tests in your test suite, it would have a success rate of 95.12%= 0.9995<sup>100</sup>.</p> <p>But what happens when you have thousands of these tests? A 60.64% success rate (0.9995<sup>1,000</sup>). It’s easy to calculate the significant impact of even a low failure rate on large scale tested applications.</p> <p><strong>But… What’s the problem? Just rerun the tests!</strong></p> <p>There are some really bad implications of ignoring flaky tests. Let's go over some of the most common ones, from least important to most.</p> <h3> 1. Wasted CI minutes (Hours? Days? Weeks?) </h3> <p>Consider the following scenario: </p> <p>You are a developer working in a team. There’s a new feature you’ve been developing for several days, and you opened a pull request wanting to merge it into the project. </p> <p>Now, your company works in a modern development workflow and runs automated tests on your code changes using your CI system. All of the product’s tests ran and failed on a test <strong>entirely unrelated</strong> to the changes you introduced. </p> <p>Since you are <strong>aware</strong> of the code changes you have made, and you <strong>know</strong> that this project has issues with non-deterministic tests, you therefore <strong>know</strong> the failing test is not your fault.</p> <p>So, you rerun the test, and it passes.</p> <p>If you don’t know your project has an issue with non-deterministic tests, you’ll probably waste even more time investigating.</p> <p><a href="proxy.php?url=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%2Fom320bhbwcin7zkwnsa8.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fom320bhbwcin7zkwnsa8.png" alt="rerun failed jobs" width="185" height="119"></a></p> <p>The problem is that this time accumulates. The longer the test workflows take, the more time is wasted—but how much time? This can be measured, assuming you track your CI test results. </p> <p>You can easily calculate the CI time wasted due to flakiness by summing up the CI time of a workflow run that had a non-deterministic result. For example:</p> <div class="table-wrapper-paragraph"><table> <thead> <tr> <th>#</th> <th>commit</th> <th>workflow_name</th> <th>os</th> <th>result</th> <th>duration</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>9f3e679</td> <td>test-part-1</td> <td>linux</td> <td>success</td> <td>10</td> </tr> <tr> <td>2</td> <td>9f3e679</td> <td>test-part-1</td> <td>linux</td> <td>fail</td> <td>7</td> </tr> </tbody> </table></div> <p>Run identifier: commit + workflow_name + os</p> <p>That’s 7 minutes of CI time wasted!</p> <h3> 2. Wasted development time </h3> <p>When a developer reruns a test, they are forced to wait (again) for the build and test time. Precious developer time is being lost.</p> <p>Even if we assume that a developer utilizes this wait time for other tasks, we still have 2 major drawbacks:<br> Loss of immediate feedback (long feedback loop).<br> Context switching—which eats away at focus and productivity.</p> <p>Unfortunately, this wasted time is much harder to measure.</p> <h3> 3. Flaky product behavior (or flaky implementation) </h3> <p>Sometimes a flaky test is only a symptom of a non-deterministic implementation. </p> <p>The same race condition that can cause test flakiness can do the same in a feature’s implementation, thus causing flaky product behavior in production. </p> <h3> 4. Alert fatigue </h3> <p>A common phenomenon in flaky test workflow is the loss of trust in the feedback you are getting. Consider this scenario:</p> <ol> <li>You push your new code</li> <li>Workflow tests run and fail</li> <li>“Oh it's that annoying flakiness again; we should fix it sometime”</li> <li>Rerun workflow, tests run and fail</li> <li>“D*%N FLAKINESS”</li> <li>Rerun workflow, tests run and fail</li> <li>Realizing that it was actually my code changes that had failed the tests</li> <li>Go back to step 1</li> </ol> <p><a href="proxy.php?url=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%2Fgwfjgm4bcfdk9k5ovy2j.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fgwfjgm4bcfdk9k5ovy2j.png" alt="multiple reruns" width="381" height="368"></a></p> <p>This harms development velocity and the developer’s experience. In an environment where it's not mandatory for tests to pass to merge a pull request, it is not uncommon for changes to merge even though they are breaking some product and tested behavior.</p> <h3> What's lost? </h3> <ul> <li>Money (Developer time, CI time)</li> <li>Development velocity</li> <li>Confidence in tests (regressing to manual testing)</li> <li>Product quality</li> <li>Developer experience</li> </ul> <h2> Causes of test flakiness </h2> <p>So now that we know the price and the pain, here are some of the causes of test flakiness.</p> <h3> 1. Poorly written test code </h3> <p>For example, interacting with DOM elements that are not yet ready, or improper use of <code>waitFor</code> functions. This is the most common case where testing is done incorrectly. Sometimes, powerful development machines (a.k.a, your local computer) hide race conditions in a test, which ends up failing on CI machines.</p> <h3> 2. Poorly written application code </h3> <p>As mentioned above, sometimes the application code itself introduces a flaky behavior. These cases are much harder to detect and debug. It could be related to communications, asynchronous code, or many other alternatives.</p> <h3> 3. Infrastructural causes </h3> <p>There are various environmental causes to blame, and they are the immediate culprit for those who write flaky tests. Such causes may be:</p> <ul> <li>Network issues: loss of connectivity, slow connection, etc.</li> <li>Hardware issues: low-performance shared virtual machines, which stress existing race conditions</li> <li>External dependencies: package manager (npm\yarn), runtime setup (i.e. Node, and other dependencies, which also suffer from some level of flakiness</li> </ul> <h3> 4. Test tools that are prone to flakiness </h3> <p>In our experience tests which use a browser are more prone to flakiness. One reason is that the browser itself is a complex piece of software with many dependencies, and it can be affected by a variety of factors - its version, operating system, and other specific configurations of the machine it is running on. </p> <h2> Key takeaways up to this point </h2> <p>Here are some points I think you should keep in mind:</p> <ul> <li>Flaky tests could occur due to many reasons and various causes. Some are test related, some production-code related, others from the infrastructure and development environment.</li> <li>They have direct and indirect implications on the development process—both technical and psychological.</li> <li>Flaky tests reduce development speed and quality if left untreated.</li> </ul> <h2> How to deal with flaky tests? </h2> <h3> 1. Collect data </h3> <p>It is much easier to communicate the costs of flakiness to your team or organization if you have data to back you up.</p> <p><a href="proxy.php?url=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%2Ffihwlb63xp7nvhhz59s3.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Ffihwlb63xp7nvhhz59s3.png" alt="Upload test results" width="468" height="192"></a></p> <h2> 2. Analyze it </h2> <h3> Workflow reruns per day bar graph </h3> <p>A bar graph that represents the overall flakiness and displays the total number of times a workflow has been restarted.<br> It helps us understand the scale of the flakiness problem and the lack of developer trust in the tests/CI.</p> <p>At <a href="proxy.php?url=https://www.codux.com/" rel="noopener noreferrer">Codux</a> we chose to count any case of workflow rerun, but you can also create a subset of this graph that shows reruns that <strong>never succeeded</strong>, which could better depict the lack of trust in your tests/CI.</p> <p>This is a general index that tells if your data correlates with your <strong>general feel</strong> of flakiness. We don’t derive tasks from it.</p> <p><a href="proxy.php?url=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%2Fthmtj66xghc1074nxpw9.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fthmtj66xghc1074nxpw9.png" alt="Reruns per day bay graph" width="800" height="172"></a></p> <ul> <li>We count rerun by identifying the commit, branch, OS, and workflow name. We call it an “entity” and count its total occurrences minus one.</li> </ul> <h3> Fail rate table </h3> <p>This is a table that calculates a test’s fail rate out of its total runs. We collect data from all branches, including development branches, and present only tests that have failed on 3 branches or more, with a minimum number of total runs.</p> <p>This table helps us find the current culprits tests. A Flaky test that fails over an arbitrary percentage of your choice (we chose 5%), is skipped, documented, and assigned to the relevant developer. This process occurs 1-2 times a week.</p> <p>This process requires reasoning and shouldn’t, in our opinion, be done automatically — for example:</p> <ul> <li>Some features have a low number of tests, so you probably wouldn’t want to lose coverage, and you might prefer, or should, add a retry on those tests.</li> <li>Some tests are more prone to failure (during development), such as end-to-end tests, so it might indicate they have a higher fail rate than they actually do.</li> </ul> <p><a href="proxy.php?url=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%2Fs27grryy46r8nxl57bj5.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fs27grryy46r8nxl57bj5.png" alt="Fail rate table" width="800" height="360"></a></p> <h3> Fail by test scatter plot (Environmental factors) </h3> <p>We’ve created a plot similar to <a href="proxy.php?url=https://engineering.atspotify.com/2019/11/test-flakiness-methods-for-identifying-and-dealing-with-flaky-tests/#:~:text=I%20discussed%20below%3A-,Odeneye,-Odeneye%20is%20a" rel="noopener noreferrer">Spotify’s Odeneye</a>. This plot helps us realize if there are some environmental or infrastructural problems. If you suspect your infrastructure is causing flakiness, try creating this dashboard.</p> <p><a href="proxy.php?url=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%2Fqkvptyayokx6hi43y7xu.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fqkvptyayokx6hi43y7xu.png" alt="Fail by test scatter plot" width="800" height="299"></a><br> _<br> Horizontal lines indicate that a test is flaky. Vertical lines indicate an issue external to the test because it shows multiple test failures in the same timeframe. _</p> <h3> 3. Run new tests multiple times </h3> <p>After noticing that newly created tests are flaky and require adjustments, we have decided to raise the bar for newly created tests and created “<strong>check-new-flaky</strong>” — a CLI tool that detects new tests and runs them multiple times.</p> <p>It detects new tests by running our test runner (mocha) programmatically, recursively extracting test names on the branch, and comparing them to master.</p> <p>Checking newly created tests reduced the new flaky tests added to the application and the need to refactor them significantly.</p> <p><a href="proxy.php?url=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%2F5tid52e9w23tajv09s2e.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F5tid52e9w23tajv09s2e.png" alt="Run new tests multiple times" width="800" height="521"></a></p> <h4> Some more benefits that we got: </h4> <ul> <li> <strong>Faster feedback loop</strong>: This test workflow runs your new tests immediately, thus letting you know if it passes without waiting for their turn within the entire test suite</li> <li> <strong>Another OS is running your test</strong>: All our tests are running on Linux, while tests/features which are considered to be operating system sensitive, also run on Windows. Using the check-new-flaky CLI, we sometimes get an indication that a test we thought wasn’t OS sensitive is actually sensitive or broken for the other operating system.</li> </ul> <p><a href="proxy.php?url=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%2Fym92rmitg4yz8vqzn7t1.jpeg" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fym92rmitg4yz8vqzn7t1.jpeg" alt="New flaky test shall not pass" width="770" height="324"></a></p> <h3> 4. Set a bar for when a test isn’t flaky </h3> <p>At first, it wasn't really clear to a developer when he fixed a flaky test. Developers would usually run a test 2-10 times before it would be labeled as not flaky and get merged to master.</p> <p>Once we declared war on test flakiness, the bar would be set to 100 consecutive runs.</p> <p>There are many ways to run a test multiple times — we used parts from the above CLI (check-new-flaky) and made it accessible via our GitHub bot.</p> <p><a href="proxy.php?url=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%2F2c308lm427dqanwswma6.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F2c308lm427dqanwswma6.png" alt="Benchmarking a test" width="800" height="613"></a></p> <h2> Does your test only fail when running on CI? </h2> <p>CI machines usually have reduced performance compared to your local development machine, thus most race conditions only show once tests are running on the CI.</p> <h3> Helping tests fail on your local machine </h3> <p>One tool that we have found to be helpful is CPU throttling emulation. <br> We use <a href="proxy.php?url=https://playwright.dev/" rel="noopener noreferrer">Playwright</a> for integration and end-to-end browser tests. It emulates slow CPUs using the <a href="proxy.php?url=https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setCPUThrottlingRate" rel="noopener noreferrer">Chrome Devtools Protocol</a> (experimental feature).<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight typescript"><code> <span class="k">import</span> <span class="kd">type</span> <span class="p">{</span> <span class="nx">ChromiumBrowserContext</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">playwright-core</span><span class="dl">'</span><span class="p">;</span> <span class="p">...</span> <span class="kd">const</span> <span class="nx">client</span> <span class="o">=</span> <span class="k">await </span><span class="p">(</span><span class="nx">page</span><span class="p">.</span><span class="nf">context</span><span class="p">()</span> <span class="k">as</span> <span class="nx">ChromiumBrowserContext</span><span class="p">).</span><span class="nf">newCDPSession</span><span class="p">(</span><span class="nx">page</span><span class="p">);</span> <span class="k">await</span> <span class="nx">client</span><span class="p">.</span><span class="nf">send</span><span class="p">(</span><span class="dl">'</span><span class="s1">Emulation.setCPUThrottlingRate</span><span class="dl">'</span><span class="p">,</span> <span class="p">{</span> <span class="na">rate</span><span class="p">:</span> <span class="mi">2</span> <span class="p">});</span> </code></pre> </div> <p><em>Rate is the slowdown factor (1 is no throttle, 2 is 2x slowdown)</em></p> <h3> Find out what’s going on with a test on the CI </h3> <p>Many testing tools today allow you to take some recordings of your tests. <br> Playwright released a <a href="proxy.php?url=https://playwright.dev/docs/trace-viewer/" rel="noopener noreferrer">tracing feature</a> on version 1.12, which records the test flow and provides us with screenshots and DOM snapshots. Since we had a significant issue with flaky tests, we immediately integrated this feature into our testing utils, allowing developers to record runs.</p> <p>We send CI tracing to a dedicated Slack channel for ease of use.</p> <p>This feature is super helpful when you have no clue why the test is failing on CI. Tracing helped us catch some unimaginable bugs that we wouldn't have caught otherwise.</p> <p><a href="proxy.php?url=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%2Fvkg4mobvpfzt90v3tcsm.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fvkg4mobvpfzt90v3tcsm.png" alt="Send CI trace files to slack" width="415" height="131"></a></p> <h2> Stop using ElementHandles. Start using Playwright Locators </h2> <p>Following the release of <a href="proxy.php?url=https://playwright.dev/docs/locators" rel="noopener noreferrer">Playwright Locators</a> and <a href="proxy.php?url=https://playwright.dev/docs/locators#locator-vs-elementhandle" rel="noopener noreferrer">ElementHandle being discouraged</a> from use, we decided to migrate our test kits and test drivers to Locators to enjoy the benefits given to us by this new API: <a href="proxy.php?url=https://playwright.dev/docs/actionability" rel="noopener noreferrer">actionability check</a>, more strictness (detailed below), and in our React application - reduced flakiness. </p> <p>From our experience, we can say that simply replacing ElementHandles with Locators in a test can resolve flakiness by itself.</p> <h3> What's wrong with ElementHandles? </h3> <p>Each <code>ElementHandle</code> refers to an actual specific DOM node. React, when trying to reconcile changes, might replace these referred DOM nodes. This is happening due to changes or as a result of components being unmounted and remounted again, making the referenced <code>ElementHandle</code> irrelevant. Keeping references to specific DOM nodes is not really needed because we usually get those references with selectors — which are agnostic to specific DOM nodes.</p> <h3> How Locators help us to get the correct DOM node </h3> <ul> <li>Locators keep the selector itself rather than a reference to a specific DOM node.</li> <li>Upon action (e.g .click()) the locator: <ul> <li>Uses the selector to query the DOM node relevant for that try</li> <li>Verifies it is actionable (attached, clickable, etc.) </li> <li>Validates there is no single-multiple mismatch.</li> <li>Repeats the process until it succeeds.</li> </ul> </li> </ul> <p>The actionability validation is batched along with the action itself as an atomic action. <br> For example, an atomic action could be: check if the button is available, visible, clickable and only then click it — meaning less communication between node and the browser.</p> <p>By doing the query and validation alongside the action, we prevent possible race conditions that could occur between waitFor -&gt; client re-render -&gt; action.</p> <h3> Some more benefits </h3> <ul> <li>Increased strictness: default locator will throw an exception if the selector matched more than one element.</li> <li>More readable errors: depicts the exact issue of why an action cannot be done instead of a failing assertion or some generic timeout.</li> </ul> <h2> Final words </h2> <p>Battling flakiness isn’t a short-term thing. </p> <p>It requires developers' awareness and care, writing tests more carefully, keeping in mind possible race conditions, and a conscience that tells them it isn’t okay to just rerun tests.</p> <p>It requires assistive tooling for testing the test itself and monitoring it.</p> <p>It requires priority, time, and guidelines — things you should receive from the technical management, thus requiring them to be aware of this issue.</p> <p>A single developer cannot change the state of flakiness — a group effort is needed.</p> <p>Flakiness is a manageable long-term battle. Empower yourself with the right tools to not only increase your development velocity, but also elevate your overall experience.</p> <h2> Sources: </h2> <ul> <li><a href="proxy.php?url=https://shopify.engineering/unreasonable-effectiveness-test-retries-android-monorepo-case-study" rel="noopener noreferrer">Shopify Engineering: The Unreasonable Effectiveness of Test Retries: An Android Monorepo Case Study</a></li> <li><a href="proxy.php?url=https://engineering.atspotify.com/2019/11/test-flakiness-methods-for-identifying-and-dealing-with-flaky-tests/" rel="noopener noreferrer">Spotify Engineering: Test Flakiness – Methods for identifying and dealing with flaky tests</a></li> <li><a href="proxy.php?url=https://testing.googleblog.com/2016/05/flaky-tests-at-google-and-how-we.html" rel="noopener noreferrer">Google Testing Blog: Flaky Tests at Google and How We Mitigate Them</a></li> <li><a href="proxy.php?url=https://library.oapen.org/viewer/web/viewer.html?file=/bitstream/handle/20.500.12657/22839/1007322.pdf?sequence=1&amp;isAllowed=y" rel="noopener noreferrer">Rethinking Productivity in Software Engineering, Edited by Caitlin Sadowski, Thomas Zimmermann - page 139 - The Cost of Context Switching</a></li> <li><a href="proxy.php?url=https://playwright.dev/docs/api/class-browsercontext#browser-context-new-cdp-session" rel="noopener noreferrer">Playwright: newCDPSession</a></li> </ul> watercooler Brief History of Frontend UI Tools and Newest Guy on the Block Pavlo Lisovyi Wed, 11 Jan 2023 18:31:55 +0000 https://dev.to/codux/brief-history-of-frontend-ui-tools-and-newest-guy-on-the-block-jm https://dev.to/codux/brief-history-of-frontend-ui-tools-and-newest-guy-on-the-block-jm <p><em>My brief history with Front-end UI Tools</em></p> <p>Front-end UI development is just writing code rendered in the browser, right? It can even be simplified a step further to say: 1. "Type code" and 2. "See the rendered output." That's it: Rinse, repeat as many times as needed, and you've got yourself the UI you're working on.</p> <p>But humans are lazy — especially software engineers. Pair that with business people happy to pay for an increase in productivity, and you get a tools arms race. </p> <p>Let's look back at some moments in the history of web UI development.</p> <p>My UI development memories started in early 2008 when, during a "Web Development 101" course at university, a good friend taught me a trick. At the time, we were having issues with HTML/CSS styling and layouts. (Holy grail layouts were tricky back then, am I right?)</p> <p><a href="proxy.php?url=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%2Fxoly0znwwby525awdlrd.gif" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fxoly0znwwby525awdlrd.gif" alt="GIF from Family Guy with Peter trying to control blinds, captioned " width="1024" height="1024"></a></p> <p>"Colorful markup," he called it. It wasn't then and isn't now a well-established term, so don't think you've missed something. </p> <p>It works like this: When creating and tweaking layouts, use all possible colors for backgrounds, borders and outlines — pink, magenta, yellow, and red. I even use <a href="proxy.php?url=https://css-tricks.com/rebbeccapurple-663399/" rel="noopener noreferrer">rebeccapurple</a> sometimes. Feels like a good use for it. </p> <p>Why? What were we doing back then, and why do some of us (<em>slowly raises hand</em>) still do it now? </p> <p>Short answer: It's a helpful trick to speed up layout development. And, I'll admit, it's also a force of habit. It's hard to get rid of old habits and pick up new tricks (no, I'm not talking about the "using Vim plugins for VSCode" level). </p> <p>As I was starting to work with Codux, which was new to me, I thought a lot about UI development — what I experienced and the history of the tools I encountered. </p> <p>Behind the simple "type code" step, there are tons of different tools, most of which are shared with other software development areas not specific to web UI development. </p> <p>In general, IDEs have made colossal progress in the last decades. Still, in my own experience, I started writing HTML tags like <code>&lt;IFRAME WIDTH=100&gt;</code> in Notepad (no pluses). Today, I have an IDE with tons of plugins and even an AI assistant. And we can't say this hasn't impacted front-end development. Of course, it has.</p> <p>Case in point: I had a friend who was adamant back in 2012 that he had professionally developed HTML and CSS and would not need JS. At least, as long as it wasn't forced on him. Not more than a year later, he was using Grunt for simple templating (I think components weren't popular yet around that time). </p> <p>CSS preprocessors needed CLI. Then, IDEs helped save some time in our dev cycle by auto-saving files. That was the first time my front-end development involved paid tools (Sublime, I'm looking at you).</p> <p>File change watchers became the norm. Any UI development piece required at least CLI usage, if not modifying configuration files and so on. Most of the packages.json of front-end projects that I worked on around that time were pure "devDependencies."</p> <p>Other tools were being developed closer to the "see the rendered output" side of the scale. And if you thought I was going to talk about Figma, you're damn right I am — but first, let's address the ancestor of all in-browser tools — development tools. </p> <p>We recently <a href="proxy.php?url=https://hacks.mozilla.org/2017/10/saying-goodbye-to-firebug/" rel="noopener noreferrer">said goodbye</a> to a sweet prince who changed everything — Firebug. Firebug and Firebug Lite pioneered a format we've all been using for over a decade — and even if you've never used them yourself, screenshots from 2006 would immediately tell you that you're looking at "dev tools." </p> <p><a href="proxy.php?url=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%2F7erogja7c6puya2vkund.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F7erogja7c6puya2vkund.png" alt="Screenshot of one of the earliest versions of Firebug" width="800" height="579"></a></p> <p>Of course, Chrome is the most significant player on the market nowadays. Its extensions system is enormous, with built-in dev tools functionality and custom tabs in dev tools. Most big framework players have their dev tools extensions already. But let's give credit to those who were there at the start. Firefox has been on the front lines of tools for web UI development since the beginning and up to today (from recent releases, I'm a fan of their <a href="proxy.php?url=https://hacks.mozilla.org/2019/01/designing-the-flexbox-inspector/" rel="noopener noreferrer">flexbox inspector</a>). </p> <p>There are also code sandboxes. <a href="proxy.php?url=https://www.smashingmagazine.com/2012/07/js-bin-built-for-sharing-education-and-real-time/" rel="noopener noreferrer">JSBin of 2012</a> is a great example. People needed a place to start developing, trying out ideas, and showcasing their code — but also be able to inspect it and play with it. As the complexity of front-end development grew, the complexity of sandboxes grew too, up to full-scale "Instant Dev Environments" like StackBlitz. </p> <p><a href="proxy.php?url=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%2Fl6ldecq5fseqlc3cv1xq.jpg" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fl6ldecq5fseqlc3cv1xq.jpg" alt="Screenshot of the old version of JS Bin, first published back in October 2008" width="800" height="576"></a></p> <p>We can't avoid talking about other types of tools — let's call them design-oriented. Like Figma, I don't want to get too much into it, as it isn't a UI development tool. Nevertheless, it's been used as one by some, and it keeps me awake at night. </p> <p>OK, so we've had a bit of nostalgia; maybe we smiled a bit about something, Googled something new, or just felt appreciative of the evolution of front-end development. But let's go back to the reason why I took you on that journey. Because of "colorful markup," remember? </p> <p>Despite the evolution of dev tools, nothing has changed enough for me to drop it. Yes, CSS is now much more powerful and, one can say, stable and predictable. IDEs are tremendous and allow autocompletion, popups with documentation, and AI-proposed code. </p> <p>But all of that was not enough to break my habit of needing to make an effort to keep my mind on the task of layout development. Switching the context between writing code and previewing output was always intact. Whatever was done outside, code always needed to be brought back manually — copied from tools and put somewhere. So if I were quick to type, with the help of the toolset behind me (Emmet, Live Templates, multiple cursors), that'd be quicker than any tool that would require me to integrate its "output" back to "my code."</p> <p>Nothing, that is, until I started using Codux (also when building Codux, but that's a story for another day). <br> <a href="proxy.php?url=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%2F425lpptw9lxa063hpoag.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F425lpptw9lxa063hpoag.png" alt="Screenshot of Codux, with sign in component opened and style panel being used" width="800" height="517"></a></p> <p>Because of what Codux does for me as a developer — it allows me to draft structure quickly, get classes on it, and start bashing together layouts — I see it with my own eyes on a preview (it is fully rendered production-grade stuff, with compiled preprocessed CSS, React components, etc.!). I can tweak nearly everything I can do via CSS on a style panel, and after all this — working on one screen with no navigation and no complex flow — I end up with proper HTML/CSS structure. When I think I'm done, I can commit, push, and start PR from Codux. </p> <p>While learning React, I needed a simple sandbox environment — and I found it without leaving Codux. I just created a new board and started coding, with a preview of rendered output and all the tools mentioned above. </p> <p>After an initial period of cautiousness, I'm embracing the next step in this evolution and dropping some old habits. </p> <p>Move aside dev tools and sandboxes. It's time for visual editors to start building the future. </p> <blockquote> <p>Attribution<br> Firebug screenshot - <a href="proxy.php?url=https://commons.wikimedia.org/wiki/File:Firebug.png#filelinks" rel="noopener noreferrer">https://commons.wikimedia.org/wiki/File:Firebug.png#filelinks</a><br> JSBin v1 - <a href="proxy.php?url=https://www.flickr.com/photos/remysharp/4284906136" rel="noopener noreferrer">https://www.flickr.com/photos/remysharp/4284906136</a></p> </blockquote> watercooler A visual is worth 1000 lines of code Arnon Kehat Mon, 12 Dec 2022 14:38:53 +0000 https://dev.to/codux/a-visual-is-worth-1000-lines-of-code-pe https://dev.to/codux/a-visual-is-worth-1000-lines-of-code-pe <h2> Why we believe in visual controllers </h2> <p>They say “a picture is worth a thousand words”. It's a saying that attempts to describe the difference between the amount of information that can be conveyed in a visual format, and the amount conveyed verbally or textually. In short, there’s no real contest. Visuals are faster and easier to use and understand than words for almost every person on the planet. This is the reason we build user interfaces in the first place—they help people interact with our systems in ways that would be difficult via text alone.<br> If you’re not convinced, try to compare the amount of time it takes you to absorb the information below, represented first as text, and then in a relatively agreed upon visual representation:</p> <p><code>Bold: on || Italics: off || Underline: off || Text color: red || Text highlight color: yellow</code></p> <p><a href="proxy.php?url=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%2Fwrtmh6i5k0xf5el1b59z.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2Fwrtmh6i5k0xf5el1b59z.png" alt="Image description" width="186" height="52"></a></p> <p>The visual representation only requires a quick, almost instantaneous scan, while the text requires a few seconds of eyeballing even for the quickest reader (not to mention the additional screen space required).</p> <p>When we want to design a new visual representation, we use the visual medium’s innate superiority to our advantage. And so, when we plan user interfaces, we use visual design tools (Figma, Sketch, Photoshop, etc.) They allow us to work with our eyes and hands to create the <strong>precise</strong> visual representation we want, all the while ignoring all other concerns (scale limitations, technical debt, performance, etc.). But their output is often completely static. It looks like our interface (often in its most basic forms), but it's miles away. To describe all its different interactions, we'd need to design it over and over again in all of its manifestations.</p> <p>When we want to build the <em>actual</em> user interface, we are limited by current tools to using text that describes our visuals, their interactions and their logic (in our case text in the form of HTML, CSS and JS code). This happens because of technological limitations, because of old habits, and because developers take upon themselves an unfair share of the burden of making the systems work. They create the logic and infrastructure, as they should, but they also create the visuals and their interactions—because they are the only ones who can.</p> <p>What we’re left with is thousands of lines of code that describe visual information, and a gap between stakeholders’ ability to directly affect the project. The textual description of the visuals is often completely opaque to the designer who designed them. Unless they are well-versed in reading and writing CSS and HTML (and many times other code flavors like React or Vue), they can point their finger at what’s wrong, but cannot fix it themselves.</p> <h3> This is where visual controllers come into play </h3> <p>Visual controllers are the set of tools that allow users to apply changes to code using their eyes and hands. For example, choosing a color via the mouse and a palette, or changing the size of a <code>div</code> by dragging its corners using the mouse.</p> <p>They enable us to forgo the translation between visual and text, allowing us to use our senses to determine what should appear, and where. There are fewer “translation” layers, and therefore, less room for error as well as a quicker output. A designer’s eyes won’t miss a misplaced button visually. The same can’t be said for textual CSS comprehension, as good as we may be with CSS.</p> <p>Visual controllers are also a great equalizer. They make design accessible and easy for people who may not know how to describe the same visuals in text format (practically anyone who has not spent years honing their front-end coding skills). They offer us a way to widen the circle of contributors, so that more of the stakeholders can directly affect the areas they own. (People who can’t make the translation from text to visual and back are often referred to as “non-technical”, but I think this is a boring and futile distinction.)</p> <p>Let’s explore another example. We have some very basic HTML code of a modal that has an attribute <code>data-open</code> which determines whether it appears on screen or not. The HTML code looks something like this:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;&lt;div</span> <span class="na">class=</span><span class="s">"modal"</span> <span class="na">data-open=</span><span class="s">"true"</span><span class="nt">&gt;</span><span class="c">&lt;!--...modal content...--&gt;</span><span class="nt">&lt;/div&gt;&lt;/div&gt;</span> </code></pre> </div> <p>But, almost no developer in their right mind would work on it this way. Indentation and line breaks are the most basic visual manipulation developers use in order to show the DOM structure in a readable way. The output is often something like this:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">&gt;</span> <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"modal"</span> <span class="na">data-open=</span><span class="s">"true"</span><span class="nt">&gt;</span> <span class="c">&lt;!--...modal content...--&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre> </div> <p>Now the structure is a lot easier to deduce thanks to a visual cue. With a visual controller we can go even further. We can show the conditional <code>data-open</code> in both of its possible states, open or closed, and use other visual aids to make this even easier to comprehend. The violet rail, for example, means a conditional DOM element in our current UI:</p> <p><a href="proxy.php?url=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%2F94mmfucmrm5xr3n3fuyl.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F94mmfucmrm5xr3n3fuyl.png" alt="Image description" width="288" height="208"></a></p> <blockquote> <p>Note: The example above is not 1:1 as in the HTML example used previously. We use React in this example because that’s what our UI currently supports, but we think it still gets the point across.</p> </blockquote> <p>The visual representation does something that the text cannot—it can reveal what other options are available. In the example above, we can see that there’s another option—the content we can display when our conditional is false. In this case and in many others, the visual controller helps us access the different options much easier than if we just had to keep them straight in our minds.</p> <h3> Conclusion </h3> <p>A good visual representation helps us form a better mental model. It can <strong>help with the understanding of hierarchies</strong> as they exist by making things that are dependent follow a visual ordering or flow. It can <strong>group together things that are related</strong> or that work together, making them easier to find and use, and improving their synergy. Most importantly, it can <strong>hint at what else is possible</strong>—what other options are available that may not jump out of the text.</p> <p>We believe that visual controllers for structure and design can be helpful to anyone who participates in the interface creation process, and that this is a big part of what drives our development and design process. We believe in visual controllers.</p> <h3> Come see what we mean </h3> <p><a href="proxy.php?url=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%2F25akoj8yqnzu6tdx2r58.png" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F25akoj8yqnzu6tdx2r58.png" alt="Image description" width="800" height="479"></a></p> <p><a href="proxy.php?url=https://codux.com/?utm_source=arnonPost1&amp;utm_medium=homepage" rel="noopener noreferrer">Codux</a> is a new visual IDE for React. It follows these principles, and combines the development and design processes into a single, faceted process. We are leveraging Codux in our everyday work to build Codux—visually editing the visual aspects of the application directly from within it. <a href="proxy.php?url=https://codux.com/download?utm_source=arnonPost1&amp;utm_medium=download" rel="noopener noreferrer">Download Codux</a> and try it out to see for yourself how it improves the UI development experience. If you want to continue the discussion, leave a comment below or join our <a href="proxy.php?url=https://discord.gg/fg3Ywsa44N" rel="noopener noreferrer">Discord server</a>.</p> designpatterns oop Introducing Codux Nadav Abrahami Mon, 05 Dec 2022 09:07:29 +0000 https://dev.to/codux/introducing-codux-15j5 https://dev.to/codux/introducing-codux-15j5 <p><strong>TL;DR</strong> - Introducing Codux, a new visual IDE for easing and accelerating the development of React projects. Learn more about it <a href="proxy.php?url=https://codux.com/?utm_source=IntroArticle&amp;utm_medium=Top" rel="noopener noreferrer">here</a>, or <a href="proxy.php?url=https://discord.gg/fg3Ywsa44N" rel="noopener noreferrer">join the conversation</a>.</p> <p>Hi, my name is Nadav Abrahami, and fifteen years ago I accidentally changed the web. I didn’t do it alone, and perhaps not all of it was unintended, but you may have heard of the result: the world’s first visual no-code website builder, Wix - the company that I co-founded.<br> Now, having been a developer for many years, I’m looking to change the web yet again. And again, not alone: with the help of a dedicated group and, most importantly, with you.</p> <p>As Wix grew and advanced, utilizing new technologies, building a low code editor required a lot of, you guessed it, coding. Throughout the process, my feeling about web development grew to be quite similar to what I thought before creating Wix: it’s too messy, involving too many tools, too much repetitiveness, and too much effort. There has to be a better way of doing this!</p> <p>And so we invented one.</p> <h2> “I Wish There Was a Button for That…” </h2> <p>As things stand now, the development process of a web application is a headache. Anyone who spent way too long switching contexts just to get a gradient right or struggling with a multilayered background knows exactly how time-consuming things can be. And many basic actions, like mocking complex data structures, wiring data to properties, or looking for the origin of a code definition, involve way too many steps. It’s repetitive and tedious. Whenever I encounter one of these cumbersome but necessary multi-step tasks, tasks which hold me from writing the real code, I find myself thinking: “I wish there was a button for that!”</p> <p>In addition, as projects grow more complex, their code becomes more and more complex. Then I find myself switching between the IDE and the browser’s dev tools, just to find out what’s going on. This is especially true when considering the various states of internal components. Sometimes a clear view of the relevant part of the code I’m writing right now would save a lot of time and effort. I always wished there’d be a button for that.</p> <p>When designers are involved, things can get even more frustrating, mainly because our means of communication are less than ideal: static designs and documents, or their own specialized tools. Whatever form the design spec is in, it’s not code. And so the developer must build the product, looks included, from scratch. The designer, meanwhile, has no way of being a part of the actual coding process, except for the occasional yell at the developer or a quiet sob in the corner. And so I find myself thinking: “Why not let them make the changes themselves? I wish there was a button for that!”</p> <p>Unsurprisingly, it turned out I wasn’t the only one wishing. Far from it. And so my team started working on this wish list, and eventually created a new product. It’s the ultimate collection of buttons-for-that. It’s the place where all our wishes come true.</p> <p>We call it Codux.</p> <h2> When You Wish Upon a Stack </h2> <p>Codux is a brand-new standalone tool from Wix designed to accelerate your development process, custom-made for developers and other related professionals. It’s a development environment that gives you a real-time visual rendering of your project, with numerous editing panels for quick modifications, and code editing where it’s relevant (or when you want to).</p> Download the <a href="proxy.php?url=https://video.wixstatic.com/video/46dc18_de809f16e7bc4791bfca487d4bb1d2da/1080p/mp4/file.mp4" rel="noopener noreferrer">MP4</a> video. <p>Codux analyzes your project to discover its UI components and general structure. It does so regardless of your code-writing style. When it makes changes - when you’ve changed a property, for example - it will write code the way you would write it. Codux is designed to be used alongside your favorite IDE. It works on React projects using TypeScript and when looking at the resulting code, you’d never know whether Codux was used or not.</p> <p>The first thing you’ll notice when opening a project in Codux is the visual rendering stage. It shows how your components look in their various states. Every change you make will be reflected there in real time, whether it was done in Codux or elsewhere. </p> <p>The stage also lets you quickly access the code related to any given component. By clicking on a component you can jump directly to its source, and quickly edit its content.</p> <p>The second thing you’ll notice is the visual controls, the property, and the styling panels. Click on an element on stage, and the panels will reflect its properties. Changing styles, adding class names, and setting properties suddenly become easy and fast. No need to spend time looking for the relevant code. WYSIWYG galore!</p> <p>The third thing is a visual representation of the render tree. It includes conditionals and data-bound repeaters. Keeping a mental model of your project - or someone else's project that you inherited - becomes much easier when you have a visual illustration to refer to. A map of the component.</p> <p><a href="proxy.php?url=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%2F4eqcmrhljdchui78fism.jpg" class="article-body-image-wrapper"><img src="proxy.php?url=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%2F4eqcmrhljdchui78fism.jpg" alt="Codux rendering a card component on stage" width="800" height="500"></a></p> <p>It’s also fully Git-integrated, providing a collaborative environment for developers and designers alike. Just imagine how great it is when a designer doesn't need to send you a list of issues, but rather just fixes those on their own, using the exact same environment that you, the developer, do. The current back and forth between you two is reduced to having an occasional lunch together. Which is, to me, the best back-and-forth there is.<br> But this is only the first step on the road leading to a revolution in the way web development is done.</p> <h2> Now Wait a Minute… </h2> <p>Just in case, let’s quickly dispense with what Codux isn’t. It’s not a no-code/low-code editor (like the Wix editor). Nor is it a replacement for your IDE. Currently, like most developers, I use an IDE of my own choice set up just the way I like it. I won’t quickly replace it with something else, and I wouldn’t expect you to do that either.</p> <h2> Back to the Future </h2> <p>Eventually, Codux will become a one-stop shop for all stakeholders in a given project, covering everything from basic design to full deployment. It will support all frameworks - React, Vue, Svelte, Web Components, you name it - and all styling solutions. What we aim to make here is no less than the easiest, most comfortable, visual, and time-saving solution for writing web applications.</p> <p>Yes, we know it’s a lot to do. But then, back when we originally created Wix, that seemed impossible as well. I think the results speak for themselves.</p> <h2> So What Now? </h2> <p>Right now, Codux can speed up your development process considerably. It works great with React, TypeScript, CSS, CSS/Sass Modules, and our very own <a href="proxy.php?url=https://stylable.io/" rel="noopener noreferrer">Stylable</a>. It’s also free of charge. So why not download it and give it a try? I’d love to hear your thoughts about it, either in a comment below or on our new <a href="proxy.php?url=https://discord.gg/fg3Ywsa44N" rel="noopener noreferrer">Discord server</a>.</p> <p>In the eternal words of Doc Brown: “Roads? Where we’re going we don’t need roads.” Well, substitute “roads” for anything that holds back your development process right now - that’s where you and I are going.</p> <p>Download Codux <a href="proxy.php?url=https://codux.com/download?utm_source=IntroArticle&amp;utm_medium=Bottom" rel="noopener noreferrer">here</a>.</p> javascript webdev discuss