<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Jonathan</title>
    <description>The latest articles on DEV Community by Jonathan (@conw_y).</description>
    <link>https://dev.to/conw_y</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F122797%2F60ac7a14-440b-415d-ba87-11ed77959570.jpg</url>
      <title>DEV Community: Jonathan</title>
      <link>https://dev.to/conw_y</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/conw_y"/>
    <language>en</language>
    <item>
      <title>We’re all full stack now</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Wed, 19 Aug 2026 07:17:27 +0000</pubDate>
      <link>https://dev.to/conw_y/were-all-full-stack-now-4l53</link>
      <guid>https://dev.to/conw_y/were-all-full-stack-now-4l53</guid>
      <description>&lt;p&gt;I've noticed a recent trend toward traditionally more specialised developers (including my own area, front end) working more full stack.&lt;/p&gt;

&lt;p&gt;Working full stack doesn't necessarily have to mean writing back end or platform code, but it might mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Being aware of, and understanding, systems further up the stack&lt;/li&gt;
&lt;li&gt;Knowing enough about the whole stack to understand other engineers and "speak their language"&lt;/li&gt;
&lt;li&gt;Being able to debug back end or platform issues or isolate issues to any part of the whole stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One tricky thing about software development is that work doesn't necessarily come in neat little packages that can be handed off to front end or back end. For example, a task that seems to be targetting user experience might end up involving a config change on the back end. Or a task that seems to be aimed at reducing server utilisation might end up involving a change to a front end API calling pattern.&lt;/p&gt;

&lt;p&gt;It's very helpful for an engineer to be able to "jump in" to another part of the stack to make a code change or "jump on" a call with a engineers of a different specialisation.&lt;/p&gt;

&lt;p&gt;This is quite a change from the very clearly demarcated front end vs. back end divide that I observed in the 2010s.&lt;/p&gt;

&lt;p&gt;What explains it? A few factors from what I can gather:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tightened budgets.&lt;/strong&gt; Thanks to the &lt;a href="https://newsletter.pragmaticengineer.com/p/zirp-engineering-practices" rel="noopener noreferrer"&gt;end of ZIRP&lt;/a&gt; and other factors, organisations want to do more work with fewer people and thus may demand more flexibility from their engineers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increasing complexity and coupling of systems.&lt;/strong&gt; Application features often require tight front end and back end coupling. For example, quickly filtering and visualising very large data sets stored on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Blurring of the lines.&lt;/strong&gt; &lt;a href="https://react.dev/reference/rsc/server-components" rel="noopener noreferrer"&gt;React Server Components&lt;/a&gt; (RSC) and associated frameworks such as &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;NextJS&lt;/a&gt; involve code executing on both server and client, while &lt;a href="https://htmx.org/" rel="noopener noreferrer"&gt;HTMX&lt;/a&gt; focuses on markup and emphasises use of full-stack/backend templating engines to generate code targetting the browser. Working with these new tools blurs the distinction between the front end and back end skill set.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security issues that cross boundaries.&lt;/strong&gt; Hackers don't care about our engineering specialisations! Exploits have increased in recent years and some of them (for example, &lt;a href="https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components" rel="noopener noreferrer"&gt;CVE-2025-55182&lt;/a&gt;) touch on the whole stack. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maturation of the industry.&lt;/strong&gt; As the current crop of front end engineers (including myself) mature and develop in their abilities, they are both able and expected to handle a broader set of problems.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Increasing efficiency of tools.&lt;/strong&gt; Spending less time fighting tools frees up engineers to focus on broader problem solving, across the stack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With quite a few independent factors seeming to be involved, this seems like a pretty robust trend, which is likely to continue.&lt;/p&gt;

&lt;p&gt;That said, I don't think the specialisations will disappear. I think there will continue to be distinct roles: front end, back end, platform, data, etc. But I think individuals in these roles will be expected to have knowledge beyond their area and be flexible about contributing in other areas.&lt;/p&gt;

</description>
      <category>fullstack</category>
    </item>
    <item>
      <title>Living systems blueprint</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Wed, 12 Aug 2026 02:35:00 +0000</pubDate>
      <link>https://dev.to/conw_y/living-systems-blueprint-6eb</link>
      <guid>https://dev.to/conw_y/living-systems-blueprint-6eb</guid>
      <description>&lt;p&gt;Imagine you're a new joiner in a corporate environment.&lt;/p&gt;

&lt;p&gt;You are faced with a complex network of systems: databases, servers, platforms, logs, etc. They're all scattered around various wiki pages, chat messages, etc.&lt;/p&gt;

&lt;p&gt;You have the challenge of trying to get your head across everything fast enough to be useful to the team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You need a map of where to find everything.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ficequx6czw7t3dwqc52m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ficequx6czw7t3dwqc52m.png" alt="Map illustration" width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you get hit with a new code review, bug report, prod support ticket or just regular task, you need to quickly see a map of the whole ecosystem, locate the specific systems that matter for the problem at hand, and jump into them instantly.&lt;/p&gt;

&lt;p&gt;This is not about conceptual or theoretical knowledge. It's about being able to actually navigate and &lt;strong&gt;access real, running systems&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read actual logs&lt;/li&gt;
&lt;li&gt;Query actual databases&lt;/li&gt;
&lt;li&gt;Push actual code changes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is where a "living systems blueprint" might come in handy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;p&gt;Borrowed from the &lt;a href="https://www.nngroup.com/articles/service-blueprints-definition/" rel="noopener noreferrer"&gt;service blueprint&lt;/a&gt;, famous in the world of UX design, this is a diagram of services.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pull-quote: A service blueprint is a diagram that visualizes the relationships between different service components — people, props (physical or digital evidence), and processes — that are directly tied to touchpoints in a specific customer journey.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;However, for our purposes as software engineers, it focusses on technical systems and services.&lt;/p&gt;

&lt;p&gt;And it has an important additional feature: &lt;strong&gt;&lt;em&gt;links&lt;/em&gt;&lt;/strong&gt;. Crucially, you want this to be more than a diagram. It should &lt;strong&gt;link&lt;/strong&gt; you to the real systems so you can actually ... you know ... &lt;em&gt;do stuff&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;You can create a diagram like this yourself, for your own use. Its sole purpose is to help you navigate your digital work environment. To diagrammatically represent all the important systems you might work with and how they connect to each-other.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diagram format
&lt;/h2&gt;

&lt;p&gt;Here's an example of such a diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48ptc7mb0kio1f2bbcpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F48ptc7mb0kio1f2bbcpu.png" alt="Example of a living systems blueprint diagram" width="800" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice the links. &lt;strong&gt;One click&lt;/strong&gt; (or copy and paste into a tool) can take you directly to the system.&lt;/p&gt;

&lt;p&gt;Say, to a CloudWatch log in Prod. Or a Postgres database in UAT. Or an API request in Postman.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn44wet56n1a6fb9nz564.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn44wet56n1a6fb9nz564.png" alt="Example of a living systems blueprint component with links" width="800" height="567"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a breakdown of the elements of the diagram:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
    &lt;tbody&gt;
        &lt;tr&gt;
            &lt;th&gt;
        Components
      &lt;/th&gt;
            &lt;td&gt;
        Depicted by a rectangle&lt;br&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxgp28royljr2ed843thp.png" alt="Components" width="360" height="324"&gt;
      &lt;/td&gt;
            &lt;td&gt;Components of the system, such as services, databases, log storage, blob storage, mobile apps, web front-ends and more.&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th&gt;
        Dependencies
      &lt;/th&gt;
            &lt;td&gt;
        Depicted by a line and arrow&lt;br&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp1sja0grlfa70kst7kod.png" alt="Components" width="360" height="262"&gt;
      &lt;/td&gt;
            &lt;td&gt;Dependencies between components, so that one "depends upon" another, such as a service depending on a database.&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;th&gt;
        Compositions
      &lt;/th&gt;
            &lt;td&gt;
        Depicted by a line and circle&lt;br&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffumcff50a4cmyllds977.png" alt="Components" width="360" height="262"&gt;

      &lt;/td&gt;
            &lt;td&gt;Compositions of components, such as parent/child or container/contained.&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Within each component rectangle, we have:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;th&gt;Icon&lt;/th&gt;
      &lt;td&gt;Quickly identify the type of the service.&lt;/td&gt;
      &lt;td&gt;Simple shape, such as a &lt;a href="https://fonts.google.com/icons" rel="noopener noreferrer"&gt;Material Design icon&lt;/a&gt;, or one of the standard vendor or cloud provider icons, say for &lt;a href="https://aws.amazon.com/architecture/icons" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, &lt;a href="https://learn.microsoft.com/en-us/azure/architecture/icons" rel="noopener noreferrer"&gt;Azure&lt;/a&gt;, &lt;a href="https://cloud.google.com/icons" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;, etc.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Title&lt;/th&gt;
      &lt;td&gt;Title of the service, as referenced in code, docs, etc.&lt;/td&gt;
      &lt;td&gt;Text in a larger font.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Type&lt;/th&gt;
      &lt;td&gt;Type of service. E.g. Web Service, Log, Database, Mobile App, etc.&lt;/td&gt;
      &lt;td&gt;Text in brackets, in a smaller font.&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;th&gt;Links&lt;/th&gt;
      &lt;td&gt;Links to access the entity or related to the entity. For a web application, it might include links to environments. For a database, it might include links to a console or a URI that can be plugged into a database client.&lt;/td&gt;
      &lt;td&gt;Text with underlined links, in a smaller font.&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Within the "Links" row, you might put links to the system itself, including multiple environments (also known as "stages") such as Development, Staging and Production.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
&lt;u&gt;Development&lt;/u&gt; | &lt;u&gt;Staging&lt;/u&gt; | &lt;u&gt;Production&lt;/u&gt;
&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Now imagine someone comes to you with the following request:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Help! Panic! I need to find out why Transfer from customer #098238 are failing in UAT!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can check your living blueprint, locate the Transfer service, see that it's connected to the Transactions database and follow the link to the Console in UAT to query recent transactions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F14qlz6mgqcfevfy3460m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F14qlz6mgqcfevfy3460m.png" alt="Screenshot of example of navigating systems blueprint" width="800" height="488"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How it helps
&lt;/h2&gt;

&lt;p&gt;Of course, you probably won't need a diagram if you're working with only a small number of systems or services on a regular basis. Likely your brain has already memorized the systems and you have browser bookmarks and the like to navigate to them quickly.&lt;/p&gt;

&lt;p&gt;But suppose you're working with many services (too many to memorize all their names), with non-trivial integration patterns, which you might need to access unexpectedly at a moment's notice. Then a living systems blueprint might help you out.&lt;/p&gt;

&lt;p&gt;A living systems blueprint can serve as a form of &lt;strong&gt;&lt;em&gt;Long Term Memory (LTM)&lt;/em&gt;&lt;/strong&gt;. Its spatial and connected form enables you to jump through a complex of network of interconnected systems and quickly locate just the resources you need via links, whether they are logs, consoles, dashboards, code files or anything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Systems blueprint sketches
&lt;/h2&gt;

&lt;p&gt;Portions of systems blueprints can be split into "sketches".&lt;/p&gt;

&lt;p&gt;You might want to do this if your main systems blueprint gets too crowded with components or if you want to depict a group of interrelated components on their own.&lt;/p&gt;

&lt;p&gt;You could share a sketch with a colleague via a link, as part of a discussion in, say, a chat thread, pull request discussion thread or ticket comment thread.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvo4ds5tg9j5uc299q6z0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvo4ds5tg9j5uc299q6z0.png" alt="Screenshot of example of sharing a systems blueprint sketch over Slack" width="800" height="724"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;p&gt;Where might you acquire the knowledge to build a living systems blueprint?&lt;/p&gt;

&lt;p&gt;Anywhere and everywhere...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask your colleagues and managers&lt;/li&gt;
&lt;li&gt;Search chat and wiki&lt;/li&gt;
&lt;li&gt;Ask AI agents&lt;/li&gt;
&lt;li&gt;Analyse code and configuration files&lt;/li&gt;
&lt;li&gt;Analyse logs and output&lt;/li&gt;
&lt;li&gt;Analyse the applications(s) themselves&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;Here are some templates you can use to create your own living systems blueprint diagram:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://conwy.co/downloads/articles/living-systems-blueprint/systems-blueprint-template.drawio" rel="noopener noreferrer"&gt;Download: Systems Blueprint Diagram - Template - Draw.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://conwy.co//downloads/articles/living-systems-blueprint/systems-blueprint-template.fig" rel="noopener noreferrer"&gt;Download: Systems Blueprint Diagram - Template - Figma&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://conwy.co//downloads/articles/living-systems-blueprint/systems-blueprint-template.rtb" rel="noopener noreferrer"&gt;Download: Systems Blueprint Diagram - Template - Miro&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And some system icon sets, depending on which (if any) vendors/providers you are using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fonts.google.com/icons" rel="noopener noreferrer"&gt;Material icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/architecture/icons/" rel="noopener noreferrer"&gt;AWS icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learn.microsoft.com/en-us/azure/architecture/icons/" rel="noopener noreferrer"&gt;Azure icons&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/icons" rel="noopener noreferrer"&gt;Google Cloud icons&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Thoughts on Tailwind</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Fri, 24 Jul 2026 03:45:31 +0000</pubDate>
      <link>https://dev.to/conw_y/thoughts-on-tailwind-5an8</link>
      <guid>https://dev.to/conw_y/thoughts-on-tailwind-5an8</guid>
      <description>&lt;p&gt;I have mixed feelings about Tailwind, having used it on a &lt;a href="https://conwy.co/work/optus" rel="noopener noreferrer"&gt;client project&lt;/a&gt;, built a &lt;a href="https://conwy.co/projects/tailwindjs" rel="noopener noreferrer"&gt;Typescript adapter&lt;/a&gt; for it and my own website for a time, before switching to &lt;a href="https://vanilla-extract.style/" rel="noopener noreferrer"&gt;Vanilla Extract&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;On one hand, it seems to have been overhyped and targeted to developers who want to throw stuff together fast, with the predictable result of messy, unmaintainable code. (But I guess the same could've been rightly said about HTML/CSS back in its early days, not to mention Java, VB, etc.)&lt;/p&gt;

&lt;p&gt;On the other hand, I think Tailwind does sit on structurally sound foundations. It implements most or all of the core features of the CSS spec, while providing shortcuts in a careful and consistent manner. In this way, it forms a nice abstraction layer above plain CSS.&lt;/p&gt;

&lt;p&gt;For example, this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"foo hover:bg-violet-600"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Is shorter and arguably more readable than this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.foo&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--violet-600&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in the right hands, Tailwind can be used to produce clean, maintainable code.&lt;/p&gt;

&lt;p&gt;I think a few best practices can go a long way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Decompose complex HTML structures.&lt;/strong&gt; Into either smaller nested elements or your framework's flavour of components (React, VueJS, Web Components, etc). Reduce the number of classes you need to deal with at once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Group and order classes in a consistent and sensible way.&lt;/strong&gt; For example: 1. Layout, 2. Background, 3. Border, 4. Foreground, 5. Animation, 6. Cursor. And order defaults before overrides: :hover, :focus, etc then defaults.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use custom theme variables for your custom brand-specific values.&lt;/strong&gt; Colours, fonts, etc. Rather than repeating the values throughout the code-base. This is similar to how variables are used in CSS.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall I think developers should use the framework/language they and their team are strongest in and be cautious about rapidly adopting any new framework/language.&lt;/p&gt;

&lt;p&gt;We should especially avoid buying into hype but consider realistic trade-offs and pivot when necessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jvns.ca/blog/2026/05/15/moving-away-from-tailwind--and-learning-to-structure-my-css-/" rel="noopener noreferrer"&gt;Blog: Moving away from Tailwind, and learning to structure my CSS • Julia EVANS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>css</category>
      <category>frontend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Front end development checklist</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Thu, 09 Jul 2026 13:22:00 +0000</pubDate>
      <link>https://dev.to/conw_y/front-end-development-checklist-39lp</link>
      <guid>https://dev.to/conw_y/front-end-development-checklist-39lp</guid>
      <description>&lt;p&gt;I thought I would share my front end development checklist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I use a checklist
&lt;/h2&gt;

&lt;p&gt;Many who worked with me will know I'm a big fan of checklists. I think a good checklist can benefit you in several ways.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It helps you get started.&lt;/strong&gt; When the task ahead seems overwhelming, such as reviewing a giant pull request, you can just start with the first checklist item and keep going from there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It helps you organise your thinking.&lt;/strong&gt; Quickly get clarity on what matters and doesn't matter. Avoid overwhelm and mental fog by following a predictable process.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It helps you identify blind-spots.&lt;/strong&gt; Common human biases (such as confirmation bias) and also your own individual biases (from past experiences).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Professional software construction and code review are complex tasks involving many elements. Research shows that the human mind is limited and can only work with a finite number of concepts at a given time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“the working memory is only capable of processing two to six things at a time ... When you are trying to solve a problem that involves too many elements ... your working memory will become “overloaded.””&lt;/p&gt;

&lt;p&gt;– &lt;a href="https://www.oreilly.com/library/view/the-programmers-brain/9781617298677/" rel="noopener noreferrer"&gt;Felienne HERMANS • The Programmer's Brain&lt;/a&gt; &lt;small&gt;(brackets mine)&lt;/small&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Additionally, research shows the human mind can be affected by cognitive flaws and biased thinking. While we are &lt;em&gt;capable&lt;/em&gt; of thinking rationally, our minds, in an attempt to conserve energy, often default to a shortcuts and heuristics, leading to critical errors. In &lt;a href="https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow" rel="noopener noreferrer"&gt;&lt;em&gt;Thinking, Fast and Slow&lt;/em&gt;&lt;/a&gt;, cognitive psychologist &lt;a href="https://en.wikipedia.org/wiki/Daniel_Kahneman" rel="noopener noreferrer"&gt;Daniel Khaneman&lt;/a&gt; describes several of these errors, such as the &lt;a href="https://en.wikipedia.org/wiki/Anchoring_effect" rel="noopener noreferrer"&gt;anchoring effect&lt;/a&gt; and the &lt;a href="https://en.wikipedia.org/wiki/Availability_heuristic" rel="noopener noreferrer"&gt;availability heuristic&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Checklist can help you overcome these biases and errors. Surgeon and author &lt;a href="https://en.wikipedia.org/wiki/Atul_Gawande" rel="noopener noreferrer"&gt;Atul Gawande&lt;/a&gt; famously wrote a book about this, demonstrating the use of checklists in mission-critical contexts from passenger flights to hospital operating rooms.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Checklists seem to provide protection against such failures. They remind us of the minimum necessary steps and make them explicit. They not only offer the possibility of verification but also instill a kind of discipline of higher performance.”&lt;/p&gt;

&lt;p&gt;– &lt;a href="https://en.wikipedia.org/wiki/The_Checklist_Manifesto" rel="noopener noreferrer"&gt;Atul GAWANDE • The Checklist Manifesto&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Similar points about checklists have been made by &lt;a href="https://www.jermainebrown.org/posts/michael-mauboussin-and-charlie-munger-checklists-tame-luck" rel="noopener noreferrer"&gt;Michael Mauboussin&lt;/a&gt; and &lt;a href="https://www.jermainebrown.org/posts/the-psychology-hacks-behind-charlie-mungers-billion-dollar-decisions" rel="noopener noreferrer"&gt;Charlie Munger&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use a development checklist
&lt;/h2&gt;

&lt;p&gt;You can use a development checklist in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Self review, prior to submitting changes.&lt;/strong&gt; Check your work against the list. This gives you an opportunity to anticipate and address any issues in advance, which reduces the code review burden on team mates and helps you to get faster approvals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code review, on others' changes.&lt;/strong&gt; Check others' work against your checklist. You can deliver useful feedback and mentor colleagues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When scanning the list and picking items to check, you can factor in what makes sense for your current team and project. In this article I provide a general front end development checklist. But you can create your own variants of the checklist, such as one for each project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automation and AI
&lt;/h2&gt;

&lt;p&gt;Checklist reviews can be partially or fully automated using AI assistants.&lt;/p&gt;

&lt;p&gt;You can feed a checklist and code changes into an AI assistant, using a &lt;a href="https://conwy.co/prompts/code-review-branch" rel="noopener noreferrer"&gt;prompt&lt;/a&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;Please review the code changes in the current branch.

Look at the checklist in the file: &lt;span class="sb"&gt;`≪~/Downloads/front-end-development-checklist.md≫`&lt;/span&gt;.

Identify which checklist items are relevant for this change.

For each relevant checklist item:
&lt;span class="p"&gt;-&lt;/span&gt; Review the code change and evaluate whether complies or not.
&lt;span class="p"&gt;-&lt;/span&gt; List any checklist items for which the code change is not compliant.
&lt;span class="p"&gt;-&lt;/span&gt; For each non-complied checklist item, provide an explanation and recommendation on how to address it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Tip: If performance is poor or costing you a lot of tokens, one solution is to break down the checklist into smaller sub-lists. Each sub-list can have a &lt;code&gt;"description"&lt;/code&gt; section, specifying the conditions under which the AI assistant read and use it. Following &lt;a href="https://docs.claude-mem.ai/progressive-disclosure" rel="noopener noreferrer"&gt;progressive disclosure&lt;/a&gt; in this way minimises context and streamlines AI performance and cost.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are other ways a checklist can be used with AI to boost productivity, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When vibe-coding, include instructions for the AI assistant to self-review its work against the checklist, either in the prompt or in context files.&lt;/li&gt;
&lt;li&gt;When using the AI assistant to generate a solution design, prompt it to self-review the solution design against the checklist.&lt;/li&gt;
&lt;li&gt;Prompt the AI assistant to go review the whole code-base against the checklist and list cases where the items are not complied with.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Continuous improvement
&lt;/h2&gt;

&lt;p&gt;Feedback you receive in code reviews can be incorporated back into the checklist.&lt;/p&gt;

&lt;p&gt;This creates a cycle of continuous improvement, making the checklist a dynamic adaptive system.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8bn4gtg16gwryardk76.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fo8bn4gtg16gwryardk76.png" alt="Diagram of cycle of continuous improvement" width="477" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Other high quality resources that can be used as sources for checklist items include books, online courses, podcasts, open-source code-bases and code review feedback from past projects&lt;/p&gt;

&lt;h2&gt;
  
  
  Precedent-based model for code reviews
&lt;/h2&gt;

&lt;p&gt;For team projects, where I add/modify a checklist item in response to feedback, I like to include a link to specific feedback comment.&lt;/p&gt;

&lt;p&gt;In subsequent code reviews, if a similar issue is being discussed, I can simply locate the closest matching guideline in the checklist and mention it in the discussion thread, with a link back to the original discussion. The linkage to the prior discussion helps to ground the discussion and provides a precedent for my decision in that specific case.&lt;/p&gt;

&lt;p&gt;I found success with this approach, in the following ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In some cases, the team agree to stay consistent with the approach suggested by the link to the prior discussion. This agreement helps to drive consensus and move the pull request toward approval.&lt;/li&gt;
&lt;li&gt;In other cases, a different approach is proposed. Agreement is found. I can improve the checklist by updating it with a link to this more recent discussion. Additionally, the team benefits by gaining more clarity over the reasoning behind the decision and also a sense of the broader approach being followed.&lt;/li&gt;
&lt;li&gt;In a few cases, there is lively debate about which approach to follow. Here, the "precedent model" helps us to organise our thinking and to keep the discussion on-track. In the worst case, I can at least demonstrate effort, think clearly, &lt;a href="https://en.wikipedia.org/wiki/Disagree_and_commit" rel="noopener noreferrer"&gt;disagree and commit&lt;/a&gt; and maintain forward momentum.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Organising and filtering the checklist
&lt;/h2&gt;

&lt;p&gt;With a checklist of 575 items and growing, you well might wonder how I have the time to go through that many items. 😂&lt;/p&gt;

&lt;p&gt;The truth is that I only use a subset of checklist items per situation. The checklist items are grouped into headings and tags which can be filtered. Using the filters, I can quickly narrow down the list to a much more manageable size.&lt;/p&gt;

&lt;p&gt;For example, when reviewing a code change that only affects styling, I can filter by styling-related tags.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Front End Development Checklist
&lt;/h2&gt;

&lt;p&gt;Here is the link to the checklist:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://conwy.co/checklists/front-end-development" rel="noopener noreferrer"&gt;👉 Front End Development Checklist&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You're welcome to &lt;a href="http://conwy.co/downloads/checklists/front-end-development/front-end-development.md" rel="noopener noreferrer"&gt;download&lt;/a&gt; the whole checklist and re-use it for your own purposes. I also welcome you to &lt;a href="https://github.com/jonathanconway/conwy/issues/new?title=Front%20end%20developer%20checklist%20-%20&amp;amp;body=&amp;amp;labels=checklist" rel="noopener noreferrer"&gt;suggest improvements&lt;/a&gt;. Or you could even &lt;a href="https://github.com/jonathanconway/conwy/fork" rel="noopener noreferrer"&gt;fork&lt;/a&gt; your own version – I might look to make this easier in future, if requested.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/The_Checklist_Manifesto" rel="noopener noreferrer"&gt;Book: The Checklist Manifesto • Atul GAWANDE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.oreilly.com/library/view/the-programmers-brain/9781617298677/" rel="noopener noreferrer"&gt;Book: The Programmer's Brain • Felienne HERMANS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Thinking,_Fast_and_Slow" rel="noopener noreferrer"&gt;Book: Thinking, Fast and Slow • Daniel KAHNEMAN&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
    <item>
      <title>Self-similar: a versatile folder pattern for code</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Thu, 07 May 2026 00:46:19 +0000</pubDate>
      <link>https://dev.to/conw_y/self-similar-a-versatile-folder-pattern-for-code-d18</link>
      <guid>https://dev.to/conw_y/self-similar-a-versatile-folder-pattern-for-code-d18</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;“The word 'isomorphism' applies when two complex structures can be mapped onto each other, in such a way that to each part of one structure there is a corresponding part in the other structure, where "corresponding" means that the two part play similar roles in their respective structures.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;– Douglas HOFSTADTER&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;a href="https://dev.to/books/godel-escher-bach"&gt;Gödel, Escher, Bach&lt;/a&gt; • Meaning and Form in Mathematics • pp. 57&lt;/small&gt;&lt;/p&gt;




&lt;p&gt;I've seen various trends come and go in code folder and file structure.&lt;/p&gt;

&lt;p&gt;The two big ones in the front-end space seem to have been:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Function-focussed.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📁 controllers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 views&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 models&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Feature-focussed.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📁 posts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 comments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 likes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There have been various efforts to combine them, such as the &lt;a href="https://feature-sliced.design/blog/frontend-folder-structure" rel="noopener noreferrer"&gt;feature-sliced design&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, we can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 posts&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 controllers&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 list.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 detail.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 create.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 delete.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 views&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 list.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 detail.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 create.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 deleted-message.tsx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 models&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 post.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 comments&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 controllers&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 views&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 models&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 likes&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall I like the above strategy. But I wanted to take it one step further.&lt;/p&gt;

&lt;p&gt;You see... sometimes I feel I want to have the best of both worlds. A bit of functional and a bit of feature.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some kinds of things seem to fit neatly into a single feature.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📁 posts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 comments&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 likes&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Other kinds of things seem to make more sense as functional pieces.

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📁 hooks&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 utils&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📁 mixins&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What I've settled on is: consistency in naming with freedom in ordering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rules
&lt;/h2&gt;

&lt;p&gt;Here are the rules of the self-similar folder pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Any word can be pluralized: &lt;code&gt;comment&lt;/code&gt; =&amp;gt; &lt;code&gt;comments&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any word can be combined with any other word (or combination of words) by a dash: &lt;code&gt;delete&lt;/code&gt;, &lt;code&gt;comment&lt;/code&gt; =&amp;gt; &lt;code&gt;delete-comment&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Any word (or combination of words) can be used to name a folder or file, at any nesting level.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;First we start with listing all of the unique "naming words" or tokens in our present file/folder structure (non-pluralized):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;border&lt;/td&gt;
      &lt;td&gt;comment&lt;/td&gt;
      &lt;td&gt;controller&lt;/td&gt;
      &lt;td&gt;create&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;delete&lt;/td&gt;
      &lt;td&gt;deleted&lt;/td&gt;
      &lt;td&gt;detail&lt;/td&gt;
      &lt;td&gt;fetch&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;get&lt;/td&gt;
      &lt;td&gt;hook&lt;/td&gt;
      &lt;td&gt;like&lt;/td&gt;
      &lt;td&gt;list&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;local&lt;/td&gt;
      &lt;td&gt;mixin&lt;/td&gt;
      &lt;td&gt;model&lt;/td&gt;
      &lt;td&gt;post&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;recessed&lt;/td&gt;
      &lt;td&gt;relieved&lt;/td&gt;
      &lt;td&gt;storage&lt;/td&gt;
      &lt;td&gt;use&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;util&lt;/td&gt;
      &lt;td&gt;view&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
      &lt;td&gt;&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Then we group similar words, based on how they seem to be used in the system and how they seem to naturally fit together. (Note that words can be re-used as many times as needed.)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: This technique has long been known in UX design as &lt;a href="https://en.wikipedia.org/wiki/Card_sorting" rel="noopener noreferrer"&gt;card sorting&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;border&lt;/code&gt;, &lt;code&gt;recessed&lt;/code&gt;, &lt;code&gt;mixin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;comment&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;post&lt;/code&gt;, &lt;code&gt;detail&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;like&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;etc...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It becomes clear that certain &lt;strong&gt;functions&lt;/strong&gt; cluster around a single &lt;strong&gt;feature&lt;/strong&gt;. For example, &lt;code&gt;post&lt;/code&gt; has &lt;code&gt;detail&lt;/code&gt;, &lt;code&gt;list&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt; and &lt;code&gt;delete&lt;/code&gt;. For these cases, we adopt feature grouping.&lt;/p&gt;

&lt;p&gt;It becomes similarly clear that certain &lt;strong&gt;functions&lt;/strong&gt; stand alone or group with other similar functions. For example, &lt;code&gt;border&lt;/code&gt; variants &lt;code&gt;recessed&lt;/code&gt; and &lt;code&gt;relieved&lt;/code&gt; are each a kind of &lt;code&gt;mixin&lt;/code&gt;, which I consider a function not a feature. For these cases, we adopt function grouping.&lt;/p&gt;

&lt;p&gt;Based on the groupings, we order the words by priority:&lt;/p&gt;

&lt;p&gt;And then nest them in order.&lt;/p&gt;

&lt;p&gt;The end-result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 posts&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 controllers&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 list.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 detail.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 create.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 delete.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 views&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 list.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 detail.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 create.tsx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 deleted-message.tsx&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 models&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 post.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 hooks&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 like-post.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 comments&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 controllers&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 views&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 models&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 likes&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;...&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 hooks&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 local-storage&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 hook&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 utils&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 fetch&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 get.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 delete.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 post.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;📁 mixins&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;📁 border&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;📄 recessed.ts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;📄 relieved.ts&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Notice that this folder structure exhibits a kind of &lt;strong&gt;self-similarity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;detail&lt;/code&gt; can appear within either &lt;code&gt;controllers&lt;/code&gt; (as a controller) or &lt;code&gt;views&lt;/code&gt; (as a view).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;controllers&lt;/code&gt;, &lt;code&gt;views&lt;/code&gt; and &lt;code&gt;models&lt;/code&gt; can appear within features &lt;code&gt;posts&lt;/code&gt;, &lt;code&gt;comments&lt;/code&gt; and &lt;code&gt;likes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hooks&lt;/code&gt; can be grouped together with other hooks, such as &lt;code&gt;local-storage&lt;/code&gt;/&lt;code&gt;hook.ts&lt;/code&gt;, but alternatively, they can be grouped with a feature, such as &lt;code&gt;posts&lt;/code&gt;/&lt;code&gt;hooks&lt;/code&gt;/&lt;code&gt;like.ts&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This self-similarity makes the folder structure extremely powerful yet flexible. Any word can be a unit of re-use, but no word is "locked in" to only appearing in one part of the structure. The form is extremely flexible, yet also extremely rigorous.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use
&lt;/h2&gt;

&lt;p&gt;It requires effort and teamwork to keep this kind of folder structure well organised. Card sorting is a high-energy, collaborative activity. So this folder structure is probably not a good fit for all scenarios.&lt;/p&gt;

&lt;p&gt;If you are a team of, say, Ruby engineers, in a medium-sized corporation, who want to play it safe and follow an established pattern, there's nothing wrong with going function-based. The enemy here is risk of losing control.&lt;/p&gt;

&lt;p&gt;If you are a startup, who need to move fast, and don't have time for any hand-wavy card-sorting UX nonsense, there's nothing wrong with going feature-based. The enemy here is time.&lt;/p&gt;

&lt;p&gt;Where I see the sweet-spot for this kind of structure is a long-term cleanup operation in a fairly complex project but a small team of very experienced polyglot programmers. Lots of collaboration, lots of care, but also lots of autonomy. The enemy here is complexity of the code itself, and a technique that accepts complexity without succumbing to it is probably the right tool for the job.&lt;/p&gt;

</description>
      <category>folders</category>
      <category>code</category>
    </item>
    <item>
      <title>There’s no rule that Agents, Skills, Tools or Commands must be LLM-based. Some use cases require 100% accuracy and determinism – for those cases, we can apply these patterns to regular code.</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Fri, 13 Mar 2026 23:43:31 +0000</pubDate>
      <link>https://dev.to/conw_y/theres-no-rule-that-agents-skills-tools-or-commands-must-be-llm-based-some-use-cases-require-36df</link>
      <guid>https://dev.to/conw_y/theres-no-rule-that-agents-skills-tools-or-commands-must-be-llm-based-some-use-cases-require-36df</guid>
      <description></description>
      <category>agents</category>
      <category>architecture</category>
      <category>programming</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Dealing with flaky tests</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Tue, 10 Mar 2026 23:21:54 +0000</pubDate>
      <link>https://dev.to/conw_y/dealing-with-flaky-tests-4d2e</link>
      <guid>https://dev.to/conw_y/dealing-with-flaky-tests-4d2e</guid>
      <description>&lt;p&gt;Flaky tests are tests that produce inconsistent and non-deterministic results, sometimes passing and sometimes failing. They can undermine the reliability of testing processes and complicate software development by masking real issues and wasting time.&lt;/p&gt;

&lt;p&gt;Flaky tests are particularly difficult to debug and fix because of their non-determinism. We cannot simply go through the usual development cycle of test → modify → re-test. This is because we cannot reliably reproduce the error on each re-test, and thus, cannot know whether any single modification has corrected it.&lt;/p&gt;

&lt;p&gt;Rather than going all in on one tactic, for flaky tests, I prefer to have a grab-bag of techniques at my disposal. I'll pick and choose one or tactics from this grab bag, based on the situation and context.&lt;/p&gt;

&lt;p&gt;In this article I'll share my grab-bag of techniques. These are tactics I've used myself or seen used by others with success.&lt;/p&gt;

&lt;p&gt;First we try to reproduce and diagnose the flakiness.&lt;/p&gt;

&lt;p&gt;Then, once we have a (hopefully firm) notion of the cause, we can apply solutions or mitigations.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt; actually fixing the flaky tests&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mitigations:&lt;/strong&gt; minimising harm, impact, cost, etc. for flaky tests we cannot fix&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The examples are in Jest and Playwright, as that is what I use in most of my work environments, but similar principles likely apply to other tools.&lt;/p&gt;

&lt;p&gt;But before diving into tactics, let's take a brief step back and look at first reproducing the flakiness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproducing flakiness
&lt;/h2&gt;

&lt;p&gt;Fundamental to addressing any kind of software bug is &lt;strong&gt;reproducing&lt;/strong&gt; it.&lt;/p&gt;

&lt;p&gt;But how do you reproduce a flaky test? As discussed above, flaky tests are difficult to reproduce consistently because their failure is non-deterministic: sometimes they fail sometimes not.&lt;/p&gt;

&lt;p&gt;There are a couple of options here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running the test repeatedly to generate a mass of failures&lt;/li&gt;
&lt;li&gt;Observing prior test failures in logs&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running the test repeatedly to generate failures
&lt;/h3&gt;

&lt;p&gt;We cannot reproduce the failure on a single run but we might have a chance on multiple runs.&lt;/p&gt;

&lt;p&gt;Assuming Jest and a &lt;code&gt;test&lt;/code&gt; script, we can use a command like this to repeatedly run a test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;1..20&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;
  npm run &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="s1"&gt;'{test_file}'&lt;/span&gt; &lt;span class="nt"&gt;--no-watch&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Failed after &lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt; attempts"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Replace &lt;code&gt;{test_file}&lt;/code&gt; with your test path and filename).&lt;/p&gt;

&lt;p&gt;Some test frameworks provide this re-running capability out-of-the-box. Here's how to do it &lt;a href="https://playwright.dev/docs/test-cli" rel="noopener noreferrer"&gt;with the Playwright CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx playwright &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;test_file&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--repeat-each&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;20 &lt;span class="nt"&gt;--fail-on-flaky-tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To increase the failure rate for reproduction purposes, we can simulate failure conditions.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simulating slower CPU and/or fewer cores&lt;/li&gt;
&lt;li&gt;Simulating lower available memory&lt;/li&gt;
&lt;li&gt;Simulating slower network speeds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These failure conditions will give us more failures, which may hopefully resolve in faster diagnosis of the cause.&lt;/p&gt;

&lt;p&gt;Some technologies for enacting these simulations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Virtual machines:&lt;/strong&gt; Running the tests in a Virtual Machine with slow configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Containers:&lt;/strong&gt; Running the tests in a container, such as a Docker container, with slow configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test runners:&lt;/strong&gt; Configuring the test runner itself to run tests slower.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Virtual machine
&lt;/h4&gt;

&lt;p&gt;Using &lt;a href="https://www.virtualbox.org" rel="noopener noreferrer"&gt;VirtualBox&lt;/a&gt; or similar, we can configure limited resources. VirtualBox allows &lt;a href="https://www.virtualbox.org/manual/topics/working-with-vms.html#ct_settings-window" rel="noopener noreferrer"&gt;limiting CPU count and processing cap&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqdkcxbfzibrgb61ncmh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvqdkcxbfzibrgb61ncmh.png" alt="Screenshot of VirtualBox with Processor Settings open" width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Container
&lt;/h4&gt;

&lt;p&gt;Using &lt;a href="https://www.docker.com" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; or similar, we can configure limited resources. Docker allows this via &lt;a href="https://docs.docker.com/engine/containers/resource_constraints/#cpu" rel="noopener noreferrer"&gt;CPU arguments&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, we could create a Dockerfile for our app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:22.12.0-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm ci
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And use it run our tests, with constrained CPU, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--cpu-shares&lt;/span&gt; 2048 tests npm run &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Test runner
&lt;/h4&gt;

&lt;p&gt;In Jest, we can try one or more of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Turning off the cache using &lt;a href="https://jestjs.io/docs/cli#--cache" rel="noopener noreferrer"&gt;&lt;code&gt;--no-cache&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Turning off multiple workers using &lt;a href="https://jestjs.io/docs/cli#--runinband" rel="noopener noreferrer"&gt;&lt;code&gt;--runInBand&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Increasing the worker count, using &lt;a href="https://jestjs.io/docs/configuration#maxworkers-number--string" rel="noopener noreferrer"&gt;&lt;code&gt;--maxWorkers&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: By running a higher number of workers than CPU cores, we effectively add more workload to our machine, causing it to run slower. This can help create conditions that will reproduce a test's flakiness.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Observing prior test failures in logs
&lt;/h3&gt;

&lt;p&gt;Alternately/additionally, we can try to gather information about the failures we have had so far.&lt;/p&gt;

&lt;p&gt;For example, if a test is being flaky in CI, we can gather log output from the CI environment and examine it to search for clues.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpolgvaqmgq66wh2ezn71.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpolgvaqmgq66wh2ezn71.jpg" alt="Screenshot of Github actions UI with test failure" width="799" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe we can learn something about the cause of the failures by observing phenomena related to the failures.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;👀 Phenomena&lt;/th&gt;
      &lt;th&gt;🚨 Implications&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;p&gt;Test fails at a certain time of day&lt;/p&gt;
      &lt;/td&gt;
      &lt;td&gt;
        Issue with date/time logic&lt;br&gt;
        Issue with resource in a particular availability zone
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;p&gt;Test fails only when modified&lt;/p&gt;
      &lt;/td&gt;
      &lt;td&gt;
        Issue with caching
      &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;p&gt;Test fails when the CI server is being heavily utilised&lt;/p&gt;
      &lt;/td&gt;
      &lt;td&gt;
        Issue with test being vulnerable to resource availability
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: &lt;strong&gt;Tip:&lt;/strong&gt; &lt;a href="https://newrelic.com/blog/log/structured-logging" rel="noopener noreferrer"&gt;Structured logging&lt;/a&gt; with detailed information about resource usage, date/time and other data can help to expose more information about test failure in logs. Such info can be logged on test failures. For example, in Jest, we can handle the &lt;a href="https://jestjs.io/docs/setup-teardown" rel="noopener noreferrer"&gt;&lt;code&gt;afterEach&lt;/code&gt;&lt;/a&gt; hook in a &lt;a href="https://jestjs.io/docs/configuration#setupfiles-array" rel="noopener noreferrer"&gt;setupFile&lt;/a&gt; and inspect [&lt;code&gt;testInfo.failed&lt;/code&gt;]. Structured logs can be generated using a library such as &lt;a href="https://github.com/structured-log/structured-log" rel="noopener noreferrer"&gt;structured-log&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Diagnosing flakiness
&lt;/h2&gt;

&lt;p&gt;Once we are able to reproduce flakiness, we can move to diagnosis, to uncover the root cause.&lt;/p&gt;

&lt;p&gt;Similar to diagnosing regular bugs, we can diagnose flaky tests by making small changes and measuring the results. With flaky test rates, rather than a single pass/fail, we measure the overall pass/fail rate. A significantly lower percentage of failures can be correlated with a code change to help uncover the cause of the failure.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: &lt;strong&gt;Warning:&lt;/strong&gt; It's important here to emphasise &lt;em&gt;failure of the test to run properly&lt;/em&gt; rather than just &lt;em&gt;failure of the test&lt;/em&gt;. A flaky test might give false-positives, passing intermittently when it should be failing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Some of the usual diagnostic techniques can be applied:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Debugging:&lt;/strong&gt; using console logging statements to observe behaviour of the test and/or application code during test execution&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Comparing recent versions:&lt;/strong&gt; using &lt;a href="https://git-scm.com/docs/git-bisect" rel="noopener noreferrer"&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt; or similar to compare results between recent versions of the branch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Process of elimination:&lt;/strong&gt; removing parts of code and measuring results, to "eliminate" irrelevant parts and identify parts that are actually causing flakiness&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solving flakiness
&lt;/h2&gt;

&lt;p&gt;This section covers possible solutions to flaky tests.&lt;/p&gt;

&lt;p&gt;Some solutions might become evident from examining output after reproducing flakiness. In other cases, it might be worth experimenting with various solutions in a "try-and-see" approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Await your elements&lt;/li&gt;
&lt;li&gt;Keep your promises&lt;/li&gt;
&lt;li&gt;Reduce test size&lt;/li&gt;
&lt;li&gt;Reduce test file size&lt;/li&gt;
&lt;li&gt;Reduce the number of workers&lt;/li&gt;
&lt;li&gt;Clean up at the end&lt;/li&gt;
&lt;li&gt;Optimise the application under test&lt;/li&gt;
&lt;li&gt;Disable resource-intensive application features when running in tests&lt;/li&gt;
&lt;li&gt;Increase timeouts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Await your elements
&lt;/h3&gt;

&lt;p&gt;Problem: Operations occur before the DOM has completed loading. For example, the test tries to click a button inside a dialog before the dialog has loaded.&lt;/p&gt;

&lt;p&gt;Solution: Wait until elements have been rendered before performing operations that depend on them.&lt;/p&gt;

&lt;p&gt;❌&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deleteButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Confirm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confirmButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;deleteButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;deleteButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dialog&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;confirmButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Confirm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;confirmButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keep your promises
&lt;/h3&gt;

&lt;p&gt;Problem: Test operations are being done before Promises on which they rely have been completed. For example, an async API call is made, but the test runs an operation that depends on the API result before the promise has completed.&lt;/p&gt;

&lt;p&gt;Solution: Wait for calls to have been made, using, say, a "completed" flag.&lt;/p&gt;

&lt;p&gt;❌&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountsApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAccounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockResolvedValue&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;
  &lt;span class="na"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountDetailsApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAccountDetails&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockResolvedValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jack's Primary Account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PrimaryAccountDetails&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jack's Primary Account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountsApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAccounts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockResolvedValue&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;
  &lt;span class="na"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isPrimary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountDetailsApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getAccountDetails&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;mockResolvedValue&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;accountId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1234&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;accountName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jack's Primary Account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PrimaryAccountDetails&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountsApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAccounts&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountsApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getAccountDetails&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveBeenCalled&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;heading&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;heading&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toHaveText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jack's Primary Account&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reduce test size
&lt;/h3&gt;

&lt;p&gt;Problem: A test is very long and, thus, times out before being completed.&lt;/p&gt;

&lt;p&gt;Solution: Reduce test size.&lt;/p&gt;

&lt;p&gt;Even if we prefer longer integration-style tests, as recommended by Kent C. Dodds in [Write fewer, longer tests], there can still be ways to reduce the size of our tests while preserving their scope.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Remove operations that are incidental and not really required for the test to exercise the code.&lt;/li&gt;
&lt;li&gt;Remove operations that are already covered by other tests.&lt;/li&gt;
&lt;li&gt;Simplify operations. For example, retrieve a DOM element directly where possible, rather than traversing multiple parent/child elements unnecessarily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Reduce test file size
&lt;/h3&gt;

&lt;p&gt;Problem: Test files are large. Processing each large file ties up system resources (especially processor usage), causing other tests to time out.&lt;/p&gt;

&lt;p&gt;Solution: Reduce test file size.&lt;/p&gt;

&lt;p&gt;One way is to split up test files by function or component.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;orders-pending.test.tsx&lt;/li&gt;
&lt;li&gt;orders-delivered.test.tsx&lt;/li&gt;
&lt;li&gt;orders-cancelled.test.tsx&lt;/li&gt;
&lt;li&gt;orders-previous.test.tsx&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or we could use numbering or lettering system.&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;small-test-01.test.tsx&lt;/li&gt;
&lt;li&gt;small-test-02.test.tsx&lt;/li&gt;
&lt;li&gt;&lt;p&gt;small-test-03.test.tsx&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;small-test-a.test.tsx&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;small-test-b.test.tsx&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;small-test-c.test.tsx&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Reduce the number of workers
&lt;/h3&gt;

&lt;p&gt;Problem: Test runners such as Jest may be greedy and fail to balance resource usage between tests when running many tests in parallel on resource-constrained environments such as CI&lt;/p&gt;

&lt;p&gt;Solution: Check the CPU configuration of the CI if possible. Try to reduce the number of simultaneously running tests by configuring the test runner.&lt;/p&gt;

&lt;p&gt;Contrary to our human intuition ("more is better") it may be better to reduce the number of simultaneously running tests. This is because test runners can be greedy and consume as many resources as possible at any given time (CPU, memory, etc). Running multiple tests at once can cause resource usage to become imbalanced, as tests compete with eachother for resources. &lt;/p&gt;

&lt;p&gt;The solution may be to reduce the maximum number of workers. Jest allows this to be configured via the &lt;a href="https://jestjs.io/docs/configuration#maxworkers-number--string" rel="noopener noreferrer"&gt;&lt;code&gt;maxWorkers&lt;/code&gt;&lt;/a&gt; setting. However, if this exceeds the number of CPU cores on the machine, the processor may be forced to split execution time between multiple threads. This may cause tests to take longer than expected to execute, resulting in timeouts. This problem may only occur on CI environments, where CPU resources may be more limited, making it tricky to identify. Try reducing the &lt;code&gt;maxWorkers&lt;/code&gt; setting or even eliminating it. (Jest defaults to the number of cores available on the machine, which is usually the safest bet.)&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean up at the end
&lt;/h3&gt;

&lt;p&gt;Problem: Tests leave behind "uncollected garbage", such as memory usage, threads, promises, etc. This slows down the test suite as a whole, making some tests flaky.&lt;/p&gt;

&lt;p&gt;Solution: Cleaning up after each test reduces resource demand on the test runner, which reduces the occurrence of flakiness.&lt;/p&gt;

&lt;p&gt;Note that the flaky test itself might not be the culprit here, but rather, some or all other test(s) as a whole generating garbage. This uncollected garbage might only be noticeable when all tests are run together in CI, not when running an individual test on its own. This can make the "uncollected garbage" issue tricky to detect. It might only be detectable by trial-and-error – say, observing resource usage on the whole test suite over repeated runs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Increase timeouts
&lt;/h3&gt;

&lt;p&gt;Most test frameworks provide allow timeouts to be configured.&lt;/p&gt;

&lt;p&gt;Increasing the timeout allows tests to run longer without failing, which may solve flakiness.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jest has &lt;a href="https://jestjs.io/docs/configuration#testtimeout-number" rel="noopener noreferrer"&gt;testTimeout&lt;/a&gt; configuration and the &lt;a href="https://jestjs.io/docs/api#testname-fn-timeout" rel="noopener noreferrer"&gt;timeout&lt;/a&gt; parameter&lt;/li&gt;
&lt;li&gt;Playwright has &lt;a href="https://playwright.dev/docs/test-timeouts" rel="noopener noreferrer"&gt;timeout configuration&lt;/a&gt; and &lt;a href="https://playwright.dev/docs/api/class-test#test-slow" rel="noopener noreferrer"&gt;test.slow()&lt;/a&gt; (called within a test)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Downside: Increasing timeouts too much or globally might allow performance issues to creep into the application. Timeouts should be increased only on flaky tests if possible, and we should find ways to enure those features continue perform adequately for end-users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimise the application under test
&lt;/h3&gt;

&lt;p&gt;Problem: The application we're testing is itself buggy or just slow. If the application itself is slow, then the automated tests that exercise it will probably also be slow, leading to flakiness.&lt;/p&gt;

&lt;p&gt;Solution: Find slow points in the application under test and optimise their performance.&lt;/p&gt;

&lt;p&gt;To find slow points, we can add timer statements to different parts of the test or application.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch user details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUserDetails&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeLog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Fetch user details&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also try rigorous &lt;a href="https://conwy.co/articles/manual-testing" rel="noopener noreferrer"&gt;manual testing&lt;/a&gt;, combined with performance tooling, such as &lt;a href="https://developer.chrome.com/docs/devtools/performance" rel="noopener noreferrer"&gt;Chrome Devtools Performance tab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Techniques to improve performance can then be applied&amp;nbsp;– see: &lt;a href="https://conwy.co/articles/react-performance" rel="noopener noreferrer"&gt;improving performance in React apps&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mitigating flakiness
&lt;/h2&gt;

&lt;p&gt;So maybe we've tried all the above and nothing has worked. In that case, we can consider mitigation – approaches that reduce the impact of the problem without solving it entirely.&lt;/p&gt;

&lt;p&gt;These might be used temporarily as an emergency resort or permanently if considered a reasonable compromise.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Disable resource-intensive application features when running in tests&lt;/li&gt;
&lt;li&gt;Reconfigure test runners&lt;/li&gt;
&lt;li&gt;Tag known flaky tests and configure accordingly&lt;/li&gt;
&lt;li&gt;Use a different kind of test&lt;/li&gt;
&lt;li&gt;Use a different method of verification&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Disable resource-intensive application features when running in tests
&lt;/h3&gt;

&lt;p&gt;Problem: Some features of our application may be resource-intensive, causing flakiness, while not offering much value in an automated testing context.&lt;/p&gt;

&lt;p&gt;Mitigation: Disable resource-intensive features for test environment only.&lt;/p&gt;

&lt;p&gt;Certain application features may be inherently resource intensive and not needed to verify correctness for a given automated test.&lt;/p&gt;

&lt;p&gt;Common examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Animations (even when implemented with CSS transitions only can create drag)&lt;/li&gt;
&lt;li&gt;Graphics (large complex DOM-heavy graphics and loaded as part of a page, such as 3D sprites in Canvas or complex SVGs)&lt;/li&gt;
&lt;li&gt;Event Subscriptions (say, to backend events via WebSockets)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These features can be disabled only for test execution, via, say, feature flags.&lt;/p&gt;

&lt;h3&gt;
  
  
  Reconfigure test runners
&lt;/h3&gt;

&lt;p&gt;Problem: Flakiness produced by resource-constrained environments is not worth the cost savings of the resource constraints.&lt;/p&gt;

&lt;p&gt;Mitigation: Increase resources to get better value for investment, such as higher developer productivity during a critical period.&lt;/p&gt;

&lt;p&gt;Depending on the cause, test flakiness might be drastically reduced in the short-term by simply beefing up resources on the test runners. Depending on the organisation, business context, timeframe, etc. this might be an optimal approach.&lt;/p&gt;

&lt;p&gt;For example, suppose a legacy system is scheduled to be decommissioned in a few weeks, with a newer, totally re-written version already performing well in canary testing and ready to be rolled out next week. If the legacy system has a lot of flaky tests, blocking developers from deploying changes during that short space of a few weeks, it might make sense to increase resources just to unblock developers. Engineer time is more valuable and costly than brute resource usage.&lt;/p&gt;

&lt;p&gt;Or suppose the business context is seasonally sensitive, such as an online retailer experiencing very high demand during holiday periods. During this period there is a high velocity of new feature releases, requiring a large number of automated tests of varying quality to run smoothly. Here, trading off resource cost for feature velocity might be worthwhile, at least during the peak period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tag known flaky tests and configure accordingly
&lt;/h3&gt;

&lt;p&gt;Problem: Flaky tests block the whole pipeline, interfering with delivery velocity.&lt;/p&gt;

&lt;p&gt;Mitigation: Separate flaky tests from non-flaky tests, to ensure that they run correctly or at least do not disrupt other tests.&lt;/p&gt;

&lt;p&gt;Many test frameworks allow tags to be applied to tests, allowing those tests to be grouped and treated as a unit, for separate execution, separate configuration, or some other kind of separation.&lt;/p&gt;

&lt;p&gt;Flaky tests, once identified, can be grouped in this way for special treatment.&lt;/p&gt;

&lt;p&gt;In Playwright, &lt;a href="https://playwright.dev/docs/test-annotations#tag-tests" rel="noopener noreferrer"&gt;test tags&lt;/a&gt; can be included in the test name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;test full report @flaky&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Jest, a similar effect can be achieved by passing a carefully written regex to the &lt;a href="https://jestjs.io/docs/cli#--testnamepatternregex" rel="noopener noreferrer"&gt;testNamePattern&lt;/a&gt; config setting:&lt;/p&gt;

&lt;p&gt;package.json:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest --testNamePattern='^(?!.*&lt;/span&gt;&lt;span class="se"&gt;\@&lt;/span&gt;&lt;span class="s2"&gt;flaky).*$'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"test-flaky"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"jest  --testNamePattern='^(.*&lt;/span&gt;&lt;span class="se"&gt;\@&lt;/span&gt;&lt;span class="s2"&gt;flaky).*$'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once separated, flaky tests might be treated in various ways:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Separate environment:&lt;/strong&gt; for example, run flaky tests on an instance with more resources (CPU, memory), faster network connections, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate lifecycle:&lt;/strong&gt; for example, run flaky tests periodically, so that they are still useful but do not block non-flaky tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separate execution style:&lt;/strong&gt; for example, re-try the flaky test more times than other tests so that they don't fail&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use a different kind of test
&lt;/h3&gt;

&lt;p&gt;Full end-to-end browser tests are known to be more flaky than traditional unit or unit-style integration tests. This is due to the performance overhead of loading a whole browser, loading the whole application at once, triggering interactions with whole DOM elements and waiting for feedback.&lt;/p&gt;

&lt;p&gt;We could instead shift some of these tests to integration-style unit tests. Described in Kent C. Dodd's famous article &lt;a href="https://kentcdodds.com/blog/static-vs-unit-vs-integration-vs-e2e-tests#integration" rel="noopener noreferrer"&gt;Static vs Unit vs Integration vs E2E Testing for Frontend Apps&lt;/a&gt;, these tests can cover entire user flows (such as logging in) while mocking the calls that could otherwise call flakiness, such as server-side API calls.&lt;/p&gt;

&lt;p&gt;Another option, for tests target intermittent but approximately deterministic behaviour, is to use fuzzy logic to verify that behaviour. For example, suppose we need to exercise some behaviour that operates on the current date and time, but for some reason cannot control the current date and time by mocking. If the test assertion does not need to have millisecond-level precision, perhaps we could instead assert against a range considered correct.&lt;/p&gt;

&lt;p&gt;❌&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getYear&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMonth&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDate&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMinutes&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;timeAfterClickPause&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSeconds&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Use a different method of verification
&lt;/h3&gt;

&lt;p&gt;If our automated test is trying to exercise something that is inherently prone to intermittent failure, within no acceptable margin of error, perhaps we need a different method of verification altogether.&lt;/p&gt;

&lt;p&gt;For example, there is probably no good way to write an automated test for generating the next Bitcoin hash on the official fork. (Until/unless we get quantum computing in the cloud, in which case, any crypto-based business model might be in jeopardy!) For this case, we would probably need to wait until we have a large and engaged enough user base and then apply observability.&lt;/p&gt;

&lt;p&gt;Various methods of verification that might fit the scenario:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Local browser tests:&lt;/strong&gt; tests that are run locally and manually by engineers, not in CI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring / observability:&lt;/strong&gt; simply provide observability into a feature without necessarily testing it. Failures can be surfaced in a dashboard or alert. Downside of this method: we risk catching a failure too late, after it has already affected a significant number of users before being discovered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual testing:&lt;/strong&gt; periodically manually test a feature in a prod-like environment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visual diff testing:&lt;/strong&gt; automatically capture screenshots of the application or component, raising alerts when differences are detected. Causes of differences can be sought by, say, comparing versions of the code base (using &lt;a href="https://git-scm.com/docs/git-bisect" rel="noopener noreferrer"&gt;&lt;code&gt;git bisect&lt;/code&gt;&lt;/a&gt; or similar).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Flaky tests undermine testing processes, developer morale and ultimately product reliability. So it's important to address them. Unfortunately fixing flaky tests can be more difficult than consistently failing tests, due to their non-determinism.&lt;/p&gt;

&lt;p&gt;Difficulties reproducing flaky tests can be addressed by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Running the test repeatedly to generate failures&lt;/li&gt;
&lt;li&gt;Observing prior test failures in logs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flaky tests can be dealt with by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solutions:&lt;/strong&gt; Optimising async or time-sensitive code, optimising test or file size to smooth test runner execution, configuring the test runner itself (workers, timeouts) or optimising the application under test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mitigations:&lt;/strong&gt; Disabling application features that tend to induce flakiness, increasing test runner resources (temporarily or permanently), separating flaky tests (say, by tagging) or opting for a different kind of test or verification.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mindfulchase.com/explore/troubleshooting-tips/troubleshooting-jest-fixing-slow-tests,-flaky-behavior,-and-memory-leaks.html" rel="noopener noreferrer"&gt;https://www.mindfulchase.com/explore/troubleshooting-tips/troubleshooting-jest-fixing-slow-tests,-flaky-behavior,-and-memory-leaks.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/docs/troubleshooting" rel="noopener noreferrer"&gt;https://jestjs.io/docs/troubleshooting&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://htalbot.dev/posts/debugging-slow-jest-tests" rel="noopener noreferrer"&gt;https://htalbot.dev/posts/debugging-slow-jest-tests&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andreyluiz.com/posts/optimizing-jest-performance/" rel="noopener noreferrer"&gt;https://andreyluiz.com/posts/optimizing-jest-performance/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/blog/2016/03/11/javascript-unit-testing-performance" rel="noopener noreferrer"&gt;https://jestjs.io/blog/2016/03/11/javascript-unit-testing-performance&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://engineering.rently.com/slow-running-jest-test-cases-how-to-optimise-it/" rel="noopener noreferrer"&gt;https://engineering.rently.com/slow-running-jest-test-cases-how-to-optimise-it/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://mindfulchase.com/explore/troubleshooting-tips/troubleshooting-jest-fixing-slow-tests,-flaky-behavior,-and-memory-leaks.html" rel="noopener noreferrer"&gt;https://mindfulchase.com/explore/troubleshooting-tips/troubleshooting-jest-fixing-slow-tests,-flaky-behavior,-and-memory-leaks.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/45087018/jest-simple-tests-are-slow" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/45087018/jest-simple-tests-are-slow&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/playwright/how-to-reproduce-ci-failures-locally-in-playwright-lfl"&gt;https://dev.to/playwright/how-to-reproduce-ci-failures-locally-in-playwright-lfl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/adobetech/improve-jest-runner-performance-a8f56708ba94" rel="noopener noreferrer"&gt;https://medium.com/adobetech/improve-jest-runner-performance-a8f56708ba94&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jestjs.io/docs/troubleshooting#tests-are-extremely-slow-on-docker-andor-continuous-integration-ci-server" rel="noopener noreferrer"&gt;https://jestjs.io/docs/troubleshooting#tests-are-extremely-slow-on-docker-andor-continuous-integration-ci-server&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>tests</category>
      <category>software</category>
    </item>
    <item>
      <title>How I built my designerly CV</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sat, 28 Feb 2026 05:43:00 +0000</pubDate>
      <link>https://dev.to/conw_y/how-i-built-my-designerly-cv-2i7m</link>
      <guid>https://dev.to/conw_y/how-i-built-my-designerly-cv-2i7m</guid>
      <description>&lt;p&gt;As a Front End Engineer – a weird yet oddly common cross between software engineer and designer – I made three self-discoveries early on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I am often looking for work&lt;/li&gt;
&lt;li&gt;I care about aesthetics&lt;/li&gt;
&lt;li&gt;I can hack almost anything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Thus, necessity proved the mother of invention, and I found myself striving to produce a CV worthy of my profession.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting a word processor
&lt;/h2&gt;

&lt;p&gt;This might seem like a problem of the 90s, but even now in an age of free browser-based apps, I still found it a non-trivial question.&lt;/p&gt;

&lt;p&gt;My requirements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free &amp;amp; open-source (I'm well off enough, but just allergic to subscriptions to big tech firms)&lt;/li&gt;
&lt;li&gt;Minimal dependency on web connectivity (I want to be able to edit my CV at short notice with limited Internet connectivity)&lt;/li&gt;
&lt;li&gt;Able to save as a regular file in the file system and share easily (say, as an email attachment, in a LinkedIn message, etc.)&lt;/li&gt;
&lt;li&gt;Able to export to common formats – both PDF and Word (in case a recruiter needs to edit in their logo)&lt;/li&gt;
&lt;li&gt;Compatible with Mac and Windows (I sometimes work on a Windows box)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With a very small learning curve &lt;a href="https://www.libreoffice.org" rel="noopener noreferrer"&gt;LibreOffice&lt;/a&gt; ticked all the boxes nicely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Layout design
&lt;/h2&gt;

&lt;p&gt;Before jumping into the word processor, I first developed a very simple pen &amp;amp; paper sketch, followed by a simple block diagram in &lt;a href="https://www.drawio.com/" rel="noopener noreferrer"&gt;Draw.io&lt;/a&gt;. This allowed me to experiment and tweak the design at a high level until I reached something I was comfortable with.&lt;/p&gt;

&lt;p&gt;I ended up with a vertically stacked set of blocks for the jobs, with each block having a two-column layout with duties on the left and details (such as start/finish dates, tech and testimonials) on the right.&lt;/p&gt;

&lt;p&gt;The design also includes a big header with some upfront features such as the tech I mainly focus on and my formal education. And an end section with side projects and short courses.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexmz4wsfjmbbyjvz3526.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fexmz4wsfjmbbyjvz3526.jpg" alt="Scan of pen &amp;amp; paper sketch to design my CV" width="300" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Tables are supposed to be anathema to web developers, aren't they? Well, this design is intended for a static document format (Word, PDF) or printed paper. It is not intended for the browser. Those who prefer or need to use a browser are welcome to peruse my &lt;a href="http://conwy.co/work" rel="noopener noreferrer"&gt;Work section on conwy.co&lt;/a&gt; or my &lt;a href="http://linkedin.com/in/jonathanconway" rel="noopener noreferrer"&gt;LinkedIn profile&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It was quite easy to use the native Table, Paragraph, Bullets and Style &amp;amp; Formatting facilities of LibreOffice, which play nicely with Word and PDF. Margins, padding and bullet style are all fully configurable, so I was get a close to perfect design.&lt;/p&gt;

&lt;p&gt;To be sure, as I was building the CV, I frequently exported to PDF and Word to check the results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffodcqxqjbe5g377mvyyt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffodcqxqjbe5g377mvyyt.png" alt="Checking exported outputs in LibreOffice, PDF and Word" width="800" height="620"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dancing with content
&lt;/h2&gt;

&lt;p&gt;Content writing for static documents is surprisingly different than for web, especially when working with a carefully crafted layout. You can't rely on the user's scroll bar so much, so text needs to be carefully crafted to fit inside the container.&lt;/p&gt;

&lt;p&gt;I found myself going back and forth between Sublime Text and LibreOffice&amp;nbsp;– the former to craft source material, the latter to edit it down to fit within the "real estate" of a formal document.&lt;/p&gt;

&lt;p&gt;A few tricks in no particular order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shortening month names (&lt;del&gt;Jan&lt;/del&gt; → Jan, etc)&lt;/li&gt;
&lt;li&gt;Abbreviating date intervals (&lt;del&gt;year&lt;/del&gt; → yr, etc)&lt;/li&gt;
&lt;li&gt;Reducing font size on whole lines (like testimonial author and date)&lt;/li&gt;
&lt;li&gt;Combining multiple details onto one line (like start/finish dates, period, industry)&lt;/li&gt;
&lt;li&gt;Removing unnecessary verbosity and non-essential grammar (trading off minor grammatical correctness for visual appeal)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyotf1ezr4la5854oa0k9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyotf1ezr4la5854oa0k9.jpg" alt="Screenshot of editing text in job description" width="600" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Finishing touches
&lt;/h2&gt;

&lt;p&gt;For a finishing touch, I wanted to use a nice fancy font for headings.&lt;/p&gt;

&lt;p&gt;After some exploration I settled on &lt;a href="https://sharingsweden.se/the-sweden-brand/brand-visual-identity/typography" rel="noopener noreferrer"&gt;Sweden Sans&lt;/a&gt;, kindly made available in the public domain by the generous tax payers of one of the most liveable countries on Earth.&lt;/p&gt;

&lt;p&gt;Web developers have it easy when it comes to interesting non-standard fonts. We just package the WOFF/TTF/ODF with our website and link to it. Or better yet, use &lt;a href="https://fonts.google.com/" rel="noopener noreferrer"&gt;Google Fonts&lt;/a&gt; and just grab a link there.&lt;/p&gt;

&lt;p&gt;A quirk of word processors like LibreOffice, when exporting an editable format like Word/DOCX, is that you cannot rely on the end-user having installed all the required fonts.&lt;/p&gt;

&lt;p&gt;But there's a hack! You can &lt;a href="https://support.microsoft.com/en-au/office/benefits-of-embedding-custom-fonts-cb3982aa-ea76-4323-b008-86670f222dbc" rel="noopener noreferrer"&gt;embed fonts&lt;/a&gt; in Word files. And it turns out &lt;a href="https://help.libreoffice.org/latest/en-US/text/shared/01/prop_font_embed.html" rel="noopener noreferrer"&gt;LibreOffice fully supports font embedding&lt;/a&gt;. Simply go to File → Properties → Font tab and check Embed fonts in the document. This worked nicely, both for exported Word/DOCX and, of course, PDF files.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Download my template
&lt;/h2&gt;

&lt;p&gt;In the spirit of Christmas I thought I'd share my CV, accompanied by a re-usable template.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;My CV • &lt;a href="https://conwy.co/downloads/articles/designerly-cv/conwy-cv.zip" rel="noopener noreferrer"&gt;Download: conwy-cv.zip&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Designerly CV Template Package • &lt;a href="https://conwy.co/downloads/articles/designerly-cv/conwy-designerly-cv-template-package.zip" rel="noopener noreferrer"&gt;Download: conwy-designerly-cv-template-package.zip&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmbcmdy3u7vjxqxneoyr.png" width="595" height="841"&gt;
      &lt;/td&gt;
      &lt;td&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffdvbskhpk53miyq511fw.png" width="595" height="841"&gt;
      &lt;/td&gt;
      &lt;td&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv5v3vvp43ysyl3dadkw.png" width="595" height="841"&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>jobs</category>
      <category>cv</category>
      <category>resume</category>
    </item>
    <item>
      <title>Generic HOC creator pattern for React</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Fri, 24 Jan 2025 02:11:00 +0000</pubDate>
      <link>https://dev.to/conw_y/generic-hoc-creator-pattern-for-react-3354</link>
      <guid>https://dev.to/conw_y/generic-hoc-creator-pattern-for-react-3354</guid>
      <description>&lt;p&gt;I've recently developed a pattern that allows me to compose two React components together in a highly decoupled way, using a generic HOC creator.&lt;/p&gt;

&lt;p&gt;In this article I'll motivate and outline the pattern, provide an example, and discuss my own implementation, &lt;code&gt;createWithHOC&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;Suppose we have an extremely &lt;strong&gt;&lt;em&gt;general&lt;/em&gt;&lt;/strong&gt; component, which we want to widely re-use by connecting it to other components in our component library.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2eon4ygsszrdfcys3x2k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2eon4ygsszrdfcys3x2k.png" alt="Diagram depicting generic and connected components" width="419" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This component is so general that most of its end-consumers will not know it even exists.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc2pseq68tq4yalkcj6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqc2pseq68tq4yalkcj6r.png" alt="Diagram depicting decoupling of generic from connected components" width="654" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, its end-consumers will need to &lt;strong&gt;&lt;em&gt;customize&lt;/em&gt;&lt;/strong&gt; its behaviour, by means of props.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp9i9m0p7exithwqljfg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkp9i9m0p7exithwqljfg.png" alt="Diagram depicting generic, connected and consumer components with props passed from consumer to generic" width="281" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern
&lt;/h2&gt;

&lt;p&gt;The pattern here is to create a HOC (Higher-Order Component) creator function, which can be applied to the &lt;strong&gt;&lt;em&gt;general&lt;/em&gt;&lt;/strong&gt; component by the &lt;strong&gt;&lt;em&gt;combined&lt;/em&gt;&lt;/strong&gt; components and exported for re-use by the &lt;strong&gt;&lt;em&gt;consumer&lt;/em&gt;&lt;/strong&gt; components.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The HOC creator function is called and passed the general component as a parameter, along with a string descriptor (more on that later).&lt;/li&gt;
&lt;li&gt;Its result is a HOC, which is exported. This exported HOC allows the general component to be combined with another component.&lt;/li&gt;
&lt;li&gt;One or more other components call this HOC, to combine themselves with the general component.&lt;/li&gt;
&lt;li&gt;Its result is a component which has almost identical props as the combined component, except that it includes one additional prop. This additional prop, named by the string descriptor from step 1, contains the props to be passed to the general component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jjbo5cgmqexas3q74kp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2jjbo5cgmqexas3q74kp.png" alt="Diagram depicting the generic HOC creator pattern outlined in the steps above" width="359" height="536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;Let's look at a realistic example to see how this pattern can be applied.&lt;/p&gt;

&lt;p&gt;Suppose we have a &lt;code&gt;Tooltip&lt;/code&gt; component, which takes a &lt;code&gt;children&lt;/code&gt; prop and a &lt;code&gt;contents&lt;/code&gt; prop. The &lt;code&gt;children&lt;/code&gt; prop will have the component that triggers the tooltip. The &lt;code&gt;contents&lt;/code&gt; prop will have the component that should be shown inside the tooltip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;TooltipProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Tooltip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TooltipProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in our component library, there are several different components that might all need to have a tooltip.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Button&lt;/li&gt;
&lt;li&gt;Checkbox&lt;/li&gt;
&lt;li&gt;Image&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each of the above three components, we want to allow their consumers to provide an optional tooltip. But we don't want to couple these three components too closely to the Tooltip component. And we want the re-use of the Tooltip component to be as easy and straight-forward as possible.&lt;/p&gt;

&lt;p&gt;First, in our &lt;code&gt;tooltip.tsx&lt;/code&gt; file, lets create and export an HOC that exposes &lt;code&gt;Tooltip&lt;/code&gt; in a re-usable manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;withTooltip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createWithHOC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Tooltip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tooltip&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that &lt;code&gt;createWithHOC&lt;/code&gt; doesn't know much about &lt;code&gt;Tooltip&lt;/code&gt;. It just receives its definition and a string descriptor - "tooltip". However, what it returns - &lt;code&gt;withTooltip&lt;/code&gt; - is very useful.&lt;/p&gt;

&lt;p&gt;Here's a simple implementation of our &lt;code&gt;Button&lt;/code&gt; component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VoidFunction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;withTooltip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Button_&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we were to consume &lt;code&gt;Button&lt;/code&gt; directly, without the &lt;code&gt;withTooltip&lt;/code&gt; wrapper, it would have the &lt;code&gt;children&lt;/code&gt; and &lt;code&gt;onClick&lt;/code&gt; props, as we would expect.&lt;/p&gt;

&lt;p&gt;However, with the &lt;code&gt;withTooltip&lt;/code&gt; wrapper call, &lt;code&gt;Button&lt;/code&gt; is now augmented with an additional &lt;code&gt;tooltip&lt;/code&gt; prop. (Named by the string "tooltip", which we originally passed to the &lt;code&gt;createWithHOC&lt;/code&gt; call in our &lt;code&gt;tooltip.tsx&lt;/code&gt; file).&lt;/p&gt;

&lt;p&gt;So now the props of &lt;code&gt;Button_&lt;/code&gt; look like this, from a consumer's point of view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Note: Fictitious interface name.&lt;/span&gt;
&lt;span class="c1"&gt;// This is just the un-named type of the props parameter of the&lt;/span&gt;
&lt;span class="c1"&gt;// `Button` component exported in the previous code sample.&lt;/span&gt;
&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ButtonProps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ReactNode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;VoidFunction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="nx"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TooltipProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when we consume &lt;code&gt;Button&lt;/code&gt;, our code can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;
      &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleDeleteClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;        &lt;span class="c1"&gt;// &amp;lt;-- &amp;lt;Button_&amp;gt; props&lt;/span&gt;
      &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;   &lt;span class="c1"&gt;// &amp;lt;-- &amp;lt;Tooltip&amp;gt; props&lt;/span&gt;
    &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      🗑️
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will have the same net effect as if we had consumed &lt;code&gt;Button_&lt;/code&gt; directly, wrapped in &lt;code&gt;Tooltip&lt;/code&gt;, passing the appropriate props to each.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tooltip&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Delete"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button_&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleDeleteClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        🗑️
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button_&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; 
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Tooltip&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Advantages
&lt;/h2&gt;

&lt;p&gt;I found three advantages of using this pattern, where applicable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Smaller consumer code&lt;/li&gt;
&lt;li&gt;Aesthetically pleasing consumer code&lt;/li&gt;
&lt;li&gt;Ability to constrain re-use of general components&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Smaller consumer code
&lt;/h3&gt;

&lt;p&gt;Consumer code is significantly smaller and less indented, as we are relying on props rather than nesting. This benefit can add up quickly when, say, rendering multiple instances of the same component type side-by-side.&lt;/p&gt;

&lt;p&gt;Here's an example of multiple buttons with tooltips:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleDeleteClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Delete&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        🗑️
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleEditClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Edit&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        📝
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleCopyClick&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;tooltip&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Copy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        📑
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Aesthetically pleasing consumer code
&lt;/h3&gt;

&lt;p&gt;Consumer code is also more aesthetically pleasing, when relying on props. It presents as a cleaner, more compact and concise list of components.&lt;/p&gt;

&lt;p&gt;Here's an example, in which we render different kinds of form controls side-by-side.&lt;/p&gt;

&lt;p&gt;Using the generic HOC pattern, each form control is passed a prop to configure one or more of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A label&lt;/li&gt;
&lt;li&gt;A default&lt;/li&gt;
&lt;li&gt;Validation logic
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"firstName"&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;First name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;TextInput&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lastName"&lt;/span&gt;
        &lt;span class="na"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;DateInput&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"dob"&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Date of birth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;validation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;maxDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nowMinus18Years&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nowMinus20Years&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Checkbox&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"registerForUpdates"&lt;/span&gt;
        &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Register for updates&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;

      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Submit
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All controls appear at a consistent level of indentation and so do their props. This is easier to read and more aesthetically pleasing than if each control was nested in a different set of containers (validation, etc) and appeared at inconsistent levels of indentation.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Aside: This could be viewed as a variant of the principle of "cleaning up irregularity", discussed in Dustin Boswell's book &lt;a href="https://www.oreilly.com/library/view/the-art-of/9781449318482/" rel="noopener noreferrer"&gt;&lt;em&gt;The Art of Readable Code&lt;/em&gt;&lt;/a&gt;, under Aesthetics (Chapter 4, pp. 37).&lt;br&gt;
The code follows a regular pattern, making it easier to visually scan and add/remove similar items (in our example, form controls).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Ability to constrain re-use of general components
&lt;/h3&gt;

&lt;p&gt;An additional advantage of this pattern is that we could, if desired, constrain the re-use of our general component.&lt;/p&gt;

&lt;p&gt;For example, we could keep the &lt;code&gt;Tooltip&lt;/code&gt; itself private to its module folder and only export the &lt;code&gt;withTooltip&lt;/code&gt; HOC. Then we could only apply &lt;code&gt;withTooltip&lt;/code&gt; to components that we are confident will work well with the &lt;code&gt;Tooltip&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As a consumer, it would be easy to determine whether a given component supports the tooltip or not – we could simply examine the available pops via our IDE's &lt;strong&gt;&lt;em&gt;auto-suggest&lt;/em&gt;&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;Here's an example with Visual Studio Code IntelliSense:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2br448iea4lx16mhk09o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2br448iea4lx16mhk09o.png" alt="IntelliSense suggesting the  raw `tooltip` endraw  prop on an  raw `IconButton` endraw  component in the conwy.co code-base" width="800" height="138"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Here's the &lt;code&gt;createWithHOC&lt;/code&gt; function I developed in Typescript for creating the &lt;code&gt;with*&lt;/code&gt; HOC creator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;omit&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createWithHOC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;THOCProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;THOCName&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;HOC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;THOCProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;hocName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;THOCName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;withHOC&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TLOCProps&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;JSX&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IntrinsicAttributes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;LOC&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TLOCProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ComponentWithLOC&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TLOCProps&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Partial&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;THOCName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;THOCProps&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hocProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hocName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;THOCProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hocProps&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hocPropsWithoutKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hocProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;THOCProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hocKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hocProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;locPropsWithoutHocProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;omit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hocName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;TLOCProps&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;HOC&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;hocPropsWithoutKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;hocKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LOC&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;locPropsWithoutHocProps&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;HOC&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LOC&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also published it as a Github Gist: &lt;a href="https://gist.github.com/jonathanconway/8a4144df3304505f720429e464641a4a" rel="noopener noreferrer"&gt;create-with-hoc.ts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Please feel free to re-use, tweak and/or share as desired. I hope someone out there finds it useful!&lt;/p&gt;

</description>
      <category>react</category>
      <category>typescript</category>
      <category>patterns</category>
    </item>
    <item>
      <title>Diagramming React code</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sat, 18 Jan 2025 10:37:00 +0000</pubDate>
      <link>https://dev.to/conw_y/diagramming-react-code-48m7</link>
      <guid>https://dev.to/conw_y/diagramming-react-code-48m7</guid>
      <description>&lt;p&gt;After working on a &lt;a href="https://conwy.co/articles/diagramming-ts" rel="noopener noreferrer"&gt;Typescript diagram format&lt;/a&gt; I wanted to focus on a React equivalent.&lt;/p&gt;

&lt;p&gt;Diagrams can be useful for various purposes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quickly sketching ideas with pen &amp;amp; paper or a diagramming too such as Draw.io&lt;/li&gt;
&lt;li&gt;Designing a solution at a high-level before writing any code&lt;/li&gt;
&lt;li&gt;Comparing different designs with eachother to decide which one is best&lt;/li&gt;
&lt;li&gt;Understanding an existing code-base by diagramming it&lt;/li&gt;
&lt;li&gt;Working out a refactoring strategy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article I'll describe a UML-influenced diagramming format for React.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;The format consistent of the following elements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Component&lt;/strong&gt; - rectangle with title and list of props&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component call&lt;/strong&gt; - caller to callee connected with solid-arrow-terminated line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component call with props&lt;/strong&gt; - caller to props and props to callee connected with solid-arrow-terminated line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component render props&lt;/strong&gt; - render prop rectangle connected to component with dot-terminated line&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function&lt;/strong&gt; - rectangle with title and list of parameters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type or interface&lt;/strong&gt; - rectangle with title and list of props&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Component
&lt;/h2&gt;

&lt;p&gt;A component is depicted with a depicted with a rectangle with &lt;code&gt;&amp;lt;&amp;lt;Component&amp;gt;&amp;gt;&lt;/code&gt; descriptor and title at the top and, optionally, props underneath.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvu0sb2j88jdhqzs0197.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmvu0sb2j88jdhqzs0197.png" alt="React diagram depicting a component" width="406" height="403"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Component call
&lt;/h2&gt;

&lt;p&gt;A component can render another component – here this is referred to as a "component call".&lt;/p&gt;

&lt;p&gt;A component call is depicted with a line from the caller component rectangle to the callee component rectangle, terminating in a filled arrow symbol.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Component call with props
&lt;/h2&gt;

&lt;p&gt;A component can pass props to another component – here this is referred to as a "component call with props".&lt;/p&gt;

&lt;p&gt;A component call with props is depicted with a line from the caller component rectangle to a props rectangle and another line from the props rectangle to the callee component rectangle, terminating in a filled arrow symbol.&lt;/p&gt;

&lt;p&gt;The props are depicted in a props rectangle, in which each prop has its own rectangle. This allows any individual prop to be linked to a type, function or component rectangle.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9aqr89x289o88q0z9lf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz9aqr89x289o88q0z9lf.png" alt="React diagram depicting a component call with props" width="402" height="627"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Component render props
&lt;/h2&gt;

&lt;p&gt;Render props are props for which we pass a React component, a function which renders a component or a React node.&lt;/p&gt;

&lt;p&gt;A render prop is depicted with a line from the prop box to a Component or Function rectangle, terminating in a dot symbol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk58stuwbq39myc1db7em.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk58stuwbq39myc1db7em.png" alt="React diagram depicting a component call with render props" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Function
&lt;/h2&gt;

&lt;p&gt;Same as in the &lt;a href="https://dev.to/articles/diagramming-ts"&gt;Typescript diagram format&lt;/a&gt;, a function is depicted with a rectangle with &lt;code&gt;&amp;lt;&amp;lt;Function&amp;gt;&amp;gt;&lt;/code&gt; descriptor and title at the top and, optionally, parameters underneath.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8p6vncstqvue8x7lssxv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8p6vncstqvue8x7lssxv.png" alt="React diagram depicting a function" width="401" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Type or interface
&lt;/h2&gt;

&lt;p&gt;Same as in the &lt;a href="https://conwy.co/articles/diagramming-ts" rel="noopener noreferrer"&gt;Typescript diagram format&lt;/a&gt;, a type or interface is depicted with a rectangle with &lt;code&gt;&amp;lt;&amp;lt;Type&amp;gt;&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;&amp;lt;Interface&amp;gt;&amp;gt;&lt;/code&gt; descriptor and title at the top and, optionally, fields underneath.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wlaqpycqfe0xpeqoiye.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4wlaqpycqfe0xpeqoiye.png" alt="React diagram depicting a function" width="770" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;composition relationship&lt;/strong&gt; between types or an inheritance relationship between interfaces is depicted with a line from the composer/inheritor to the composed/inherited type/interface, terminating in an empty arrow symbol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6niccjr9m6zfqbnhlo63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6niccjr9m6zfqbnhlo63.png" alt="React diagram depicting type composition and interface inheritance" width="764" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;reference relationship&lt;/strong&gt; between two components, functions, types or interfaces is depicted with a line from the referencer to the referenced, terminating in an arrow symbol.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt8vh7dcohipoo5wpwqu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxt8vh7dcohipoo5wpwqu.png" alt="React diagram depicting an interface reference" width="399" height="318"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: contacts list
&lt;/h2&gt;

&lt;p&gt;Here's an example of a React diagram depicting components that make up a contacts list.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ContactsList component&lt;/li&gt;
&lt;li&gt;ContactsList -&amp;gt; ContactListItem component call with render props&lt;/li&gt;
&lt;li&gt;ContactsListItem component&lt;/li&gt;
&lt;li&gt;ContactPhone component&lt;/li&gt;
&lt;li&gt;ContactEmail component&lt;/li&gt;
&lt;li&gt;Contact interface&lt;/li&gt;
&lt;li&gt;getContacts function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F084i0jwsctaf5gp8v5f0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F084i0jwsctaf5gp8v5f0.png" alt="React diagram depicting a function" width="800" height="705"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Downloads
&lt;/h2&gt;

&lt;p&gt;To make it easier to use this format, I've implemented downloadable templates for common diagramming tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mermaid • &lt;a href="https://conwy.co/downloads/articles/diagramming-react/diagramming-react.mermaid.md" rel="noopener noreferrer"&gt;Download: diagramming-react.mermaid.md&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Draw.io • &lt;a href="https://conwy.co//downloads/articles/diagramming-react/diagramming-react.drawio" rel="noopener noreferrer"&gt;Download: diagramming-react.drawio&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Figma • &lt;a href="https://conwy.co//downloads/articles/diagramming-react/diagramming-react.fig" rel="noopener noreferrer"&gt;Download: diagramming-react.fig&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>react</category>
      <category>diagramming</category>
    </item>
    <item>
      <title>AI for developers</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Thu, 09 Jan 2025 03:00:00 +0000</pubDate>
      <link>https://dev.to/conw_y/ai-for-developers-nj8</link>
      <guid>https://dev.to/conw_y/ai-for-developers-nj8</guid>
      <description>&lt;p&gt;Over the last couple of years, I've sprinkled AI usage through various typical software engineering activities.&lt;/p&gt;

&lt;p&gt;The most common tools (and what I tend to use) have been &lt;a href="https://github.com/settings/copilot" rel="noopener noreferrer"&gt;Copilot&lt;/a&gt; and &lt;a href="http://chatgpt.com" rel="noopener noreferrer"&gt;Copilot Chat&lt;/a&gt;, but some free tools such as &lt;a href="https://marketplace.visualstudio.com/items?itemName=ex3ndr.llama-coder" rel="noopener noreferrer"&gt;Llama Coder&lt;/a&gt; are beginning to become more mainstream.&lt;/p&gt;

&lt;p&gt;In this article I'll summarise the scenarios where I found AI useful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing code faster with autocomplete&lt;/li&gt;
&lt;li&gt;Getting help with error messages&lt;/li&gt;
&lt;li&gt;Getting help with technical problems&lt;/li&gt;
&lt;li&gt;Writing bash scripts&lt;/li&gt;
&lt;li&gt;Generating unit test code&lt;/li&gt;
&lt;li&gt;Summarising conversations&lt;/li&gt;
&lt;li&gt;Code reviews&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing code faster with autocomplete
&lt;/h2&gt;

&lt;p&gt;AI-powered autosuggest was a great help when I had to write a chunk of code that's necessarily verbose, but not unique to my problem.&lt;/p&gt;

&lt;p&gt;A perfect example is given on Copilot extention homepage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24z3ih5i0rtsqe7429xz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F24z3ih5i0rtsqe7429xz.png" alt="Example: Copilot JS suggest for calculating days between two dates" width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting help with error messages
&lt;/h2&gt;

&lt;p&gt;Pasting an error message into Copilot Chat, prefixed with &lt;code&gt;/fix&lt;/code&gt;, can provide good guidance on the cause and fix for a given error.&lt;/p&gt;

&lt;p&gt;When the responses given aren't suitable, follow-up prompts such as "can you suggest some other alternatives" can help.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxp07ybc5n0a2wazygmpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxp07ybc5n0a2wazygmpc.png" alt="Example: Copilot Inline Chat Error Message example" width="800" height="393"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting help with technical problems
&lt;/h2&gt;

&lt;p&gt;Similar to error messages, I found that expressing a technical problem in an AI-powered chat can yield some good ideas and solutions.&lt;/p&gt;

&lt;p&gt;This worked even better when I followed up the initial question with clarifications and/or challenged the AI to think of additional ideas or alternatives.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0u4g7qn16nteo4h8iik.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0u4g7qn16nteo4h8iik.png" alt="Example: Copilot Quick Chat feature" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing bash scripts
&lt;/h2&gt;

&lt;p&gt;Bash scripts can perform many kinds of general tasks. Once you have a script that does what you want, you can re-use it any number of times.&lt;/p&gt;

&lt;p&gt;A specific need I had was to see all the authors who had edited a particular file in a Git repository. With the help of ChatGPT I was able to write a script that achieved what I wanted.&lt;/p&gt;

&lt;p&gt;Here's the prompt I used:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Copyable: Please write a bash script which finds and lists all the unique authors who have edited the given file in the current Git repository. The given file will be specified as the first parameter to the bash script.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I've published the resulting script as a Github Gist: &lt;a href="https://gist.github.com/jonathanconway/71d5413cce30f43d3182fdfdc46c4723" rel="noopener noreferrer"&gt;list_authors.sh&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The script takes as an argument the path to a file in that repo. It then uses &lt;code&gt;git log&lt;/code&gt; on the current repository to find all the authors who have edited the file, &lt;code&gt;sort&lt;/code&gt;s and &lt;code&gt;uniq&lt;/code&gt;s them and outputs them to stdout.&lt;/p&gt;

&lt;p&gt;Here's a sample of the output from running it on &lt;a href="https://github.com/vercel/next.js/blob/fd0bc9466e42ec313ce92c58e2a5c2c157e63f54/packages/next/src/shared/lib/head.tsx" rel="noopener noreferrer"&gt;a file in the NextJS repository&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ list_authors packages/next/src/shared/lib/head.tsx
Authors who have edited 'packages/next/src/shared/lib/head.tsx':
Adam Stankiewicz &amp;lt;sheerun@sher.pl&amp;gt;
Filipe Medeiros &amp;lt;filipesilvamedeiros@gmail.com&amp;gt;
Gerald Monaco &amp;lt;gerald@gmonaco.me&amp;gt;
JJ Kasper &amp;lt;jj@jjsweb.site&amp;gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generating unit test code
&lt;/h2&gt;

&lt;p&gt;I sometimes use GitHub Copilot to generate unit tests for a particular code file I am working on.&lt;/p&gt;

&lt;p&gt;This can be done by opening the file under test, pressing Cmd+I and asking Copilot to write a unit test for the current file.&lt;/p&gt;

&lt;p&gt;I would use a prompt like: &lt;strong&gt;"Please write a unit test for this file"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I use this is mainly to set up the scaffolding, such as &lt;code&gt;describe&lt;/code&gt; and &lt;code&gt;it&lt;/code&gt; blocks and basic assertions. Usually I need to tidy up a few things here and there, but the AI is surprisingly good at generating decent test code.&lt;/p&gt;

&lt;p&gt;Another technique is to provide just the code for one function or code block and ask AI to generate test code for it, listing a few test cases.&lt;/p&gt;

&lt;p&gt;Here's an example of a prompt I might use:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Copyable: Please write a Jest unit test for a Typescript function.&lt;/p&gt;

&lt;p&gt;The unit test should have the following it blocks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;returns a list of dates between the two dates passed&lt;/li&gt;
&lt;li&gt;returns an empty array if two identical dates are passed&lt;/li&gt;
&lt;li&gt;throws an error if null is passed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Please write tests that satisfy the above it blocks.&lt;/p&gt;

&lt;p&gt;Here is the Typescript function:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;\&lt;/code&gt;`&lt;/p&gt;

&lt;p&gt;function calculateDaysBetweenDates (begin, end) {&lt;/p&gt;

&lt;p&gt;var beginDate = new Date (begin) ;&lt;/p&gt;

&lt;p&gt;var endDate = new Date(end) ;&lt;/p&gt;

&lt;p&gt;var days = Math. round (endDate - beginDate) / (1000 * 60 * 60 * 24) ) ;&lt;/p&gt;

&lt;p&gt;return days;&lt;/p&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;p&gt;`&lt;code&gt;\&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Summarising conversations
&lt;/h2&gt;

&lt;p&gt;Some colleagues and I had a long back-and-forth about code reviews in a Github discussion.&lt;/p&gt;

&lt;p&gt;When it came time to summarise all these in a meeting, I found that AI-powered chat was a great way to summarise the discussion and get everyone's thoughts on what we had discussed.&lt;/p&gt;

&lt;p&gt;It looks like ChatGPT now has a quick Summarise button for this purpose.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F86hn7erg1jwvh1m04wyg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F86hn7erg1jwvh1m04wyg.png" alt="Example: ChatGPT summarise button" width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code reviews
&lt;/h2&gt;

&lt;p&gt;When reviewing pull requests I've been experimenting with various ways of asking AI help me.&lt;/p&gt;

&lt;p&gt;One way I found useful was to have AI review and summarise the changes and overall quality.&lt;/p&gt;

&lt;p&gt;Here are the steps I took for doing this with Copilot Chat:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Generate a diff between the pull request source and destination branches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;git checkout &amp;lt;source-branch&amp;gt;&lt;/code&gt; to checkout the branch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;git diff &amp;lt;destination-branch&amp;gt; | pbcopy&lt;/code&gt; to run the diff and copy the output&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Paste the whole diff into a new empty tab in VS Code&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Press Cmd+I and prompt Copilot: &lt;strong&gt;"Please review these code changes"&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's a sample of the kinds of results I tended to get (anonymised):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The code changes introduce several new features and modifications to the healthcare application. Here are the key points:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Localization Updates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Added new localization strings for transfer confirmations in &lt;code&gt;en-AU.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Removed Mock Initialization:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed the conditional import of mock initialization in &lt;code&gt;layout.tsx&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;New Patient Transfer Form Submission Logic:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Created a new file &lt;code&gt;submit.ts&lt;/code&gt; to handle the patient transfer form submission logic.&lt;/li&gt;
&lt;li&gt;This includes creating a &lt;code&gt;Ward&lt;/code&gt; object, making a patient transfer request, and handling different states of the transfer (success, delay, error, no-response)&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Transfer Form Tests:

&lt;ul&gt;
&lt;li&gt;Updated &lt;code&gt;transfer-form.test.tsx&lt;/code&gt; to reflect changes in patient name and ward number and added tests for the new patient transfer form submission logic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Overall, these changes enhance the patient transfer functionality by providing detailed feedback on the transfer status and improving the test coverage for various scenarios.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not as good as a human reviewer, but not useless either.&lt;/p&gt;

&lt;p&gt;And not bad given the tiny effort on my part!&lt;/p&gt;

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

&lt;p&gt;While I feel AI might have been a little over-hyped, I think there's still merit in becoming familiar with it and using it where it fits.&lt;/p&gt;

&lt;p&gt;It seems likely that AI adoption among software engineers will grow, so it will be advantageous to be familiar and conversant with the tooling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading and viewing
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/watch?v=CwAzIpc4AnA" rel="noopener noreferrer"&gt;&lt;em&gt;Pragmatic techniques to get the most out of GitHub Copilot&lt;/em&gt;&lt;/a&gt; • Patrick CHANEZON, Burke HOLLAND, Brigit MURTAUGH, Isidor NIKOLIC, Allison WEINS, Martin WOODWARD&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://simonwillison.net/2024/Sep/20/using-llms-for-code/" rel="noopener noreferrer"&gt;&lt;em&gt;Notes on using LLMs for code&lt;/em&gt;&lt;/a&gt;&amp;nbsp;• Simon WILSON&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>development</category>
      <category>coding</category>
    </item>
    <item>
      <title>AI user interface patterns</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Fri, 04 Oct 2024 03:01:52 +0000</pubDate>
      <link>https://dev.to/conw_y/ai-user-interface-patterns-25n1</link>
      <guid>https://dev.to/conw_y/ai-user-interface-patterns-25n1</guid>
      <description>&lt;p&gt;As AI capabilities become increasingly available to everyday development teams, it might be useful to explore way to surface these capabilities to end-users.&lt;/p&gt;

&lt;p&gt;This article describes some user interface patterns for surfacing AI.&lt;/p&gt;

&lt;p&gt;Using &lt;strong&gt;&lt;em&gt;"treat it like a person"&lt;/em&gt;&lt;/strong&gt; as a core principle, I provide patterns, examples (including in-the-wild cases) and how they mimic humans.&lt;/p&gt;

&lt;h2&gt;
  
  
  Principles
&lt;/h2&gt;

&lt;p&gt;As the definition of "AI" is still somewhat in flux, I want to assume a definition for the purposes of this article:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;The capability of software/machines to do things that are normally thought of a human, in a rational way.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The fact AI mimics human thinking/behaviour suggests that we user interfaces should expose AI capabilities in a human way.&lt;/p&gt;

&lt;p&gt;As Ethan Mollick puts it in the book &lt;a href="https://www.penguin.com.au/books/co-intelligence-9780753560778" rel="noopener noreferrer"&gt;&lt;em&gt;Co-Intelligence&lt;/em&gt;&lt;/a&gt; (bold mine):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;AI doesn’t act like software, but it does act like a human being. I’m not suggesting that AI systems are sentient like humans, or that they will ever be. Instead, I’m proposing a pragmatic approach: &lt;strong&gt;&lt;em&gt;treat AI as if it were human&lt;/em&gt;&lt;/strong&gt; because, in many ways, it behaves like one. This mindset, which echoes my “treat it like a person” principle of AI, can significantly improve your understanding of how and when to use AI in a practical, if not technical, sense.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The patterns I've encountered are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inline suggestion&lt;/li&gt;
&lt;li&gt;Single-agent chat&lt;/li&gt;
&lt;li&gt;Multi-agent chat&lt;/li&gt;
&lt;li&gt;Prompting and nudging controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at them in detail...&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern: Inline suggestion
&lt;/h2&gt;

&lt;p&gt;While performing a task, relevant ideas are displayed nearby in text or even imagery.&lt;/p&gt;

&lt;p&gt;The user might be invited to provide input, such as selecting between variants or filling in a blank. (See Fill-in-the-blanks pattern)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; As a product owner enters a story into a task tracking system, the AI suggests edge cases they didn't yet consider.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-inline-suggestion.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-inline-suggestion.svg" alt="Example: inline suggestion for a product owner entering a task" width="514" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the wild:&lt;/strong&gt; Code assistance plug-ins in IDEs (such as &lt;a href="https://www.genieai.co/" rel="noopener noreferrer"&gt;Genie AI&lt;/a&gt;, &lt;a href="https://aws.amazon.com/q/" rel="noopener noreferrer"&gt;Amazon Q&lt;/a&gt;, &lt;a href="https://codegpt.co/" rel="noopener noreferrer"&gt;CodeGPT&lt;/a&gt;, &lt;a href="https://codeium.com/" rel="noopener noreferrer"&gt;Codeium&lt;/a&gt; and &lt;a href="https://github.com/ex3ndr/llama-coder" rel="noopener noreferrer"&gt;Llama Coder&lt;/a&gt;) and dedicated IDEs (such as &lt;a href="http://cursor.com" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt;). Email and text message completion, as seen in &lt;a href="https://mail.google.com/" rel="noopener noreferrer"&gt;Gmail&lt;/a&gt;, &lt;a href="http://linkedin.com/" rel="noopener noreferrer"&gt;LinkedIn messaging&lt;/a&gt; and &lt;a href="https://en.wikipedia.org/wiki/Messages_(Apple)" rel="noopener noreferrer"&gt;Apple Messages&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s0a4v6rpftw5z7m2d8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s0a4v6rpftw5z7m2d8j.png" alt="In the wild: reply suggestion on LinkedIn" width="800" height="317"&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mimics:&lt;/strong&gt; 🧍 Colleague, mentor, friend, etc. sitting nearby verbally offering a suggestion and/or physically pointing to a part of the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern: Single-agent chat
&lt;/h2&gt;

&lt;p&gt;A specialised chat bot is available for back-and-forth discussion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt;: Customer on an e-commerce website starts entering a question about a product. A specialised chat-bot replies with a detailed response. An input box allows the customer to respond with a follow-up question.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-single-agent-chat.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-single-agent-chat.svg" alt="Example: specialised single-agent chat bot on an e-commerce website" width="401" height="547"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the wild:&lt;/strong&gt; "Have a question?" feature on &lt;a href="http://hotels.com/" rel="noopener noreferrer"&gt;Hotels&lt;/a&gt;, "QnaBot" on &lt;a href="http://amazon.com/" rel="noopener noreferrer"&gt;Amazon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6op6wkd8iny7z069prnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6op6wkd8iny7z069prnw.png" alt="In the wild: " width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbuonin56zok5k9z7i16.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnbuonin56zok5k9z7i16.png" alt="In the wild: " width="572" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mimics:&lt;/strong&gt; 💬 Colleague, mentor, customer service, etc. communicating with the user via chat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern: Multi-agent chat
&lt;/h2&gt;

&lt;p&gt;Multiple chat bots appear in the same chat window. Each bot has a different persona and perspective, and only contributes where applicable.&lt;/p&gt;

&lt;p&gt;By splitting AI responses among multiple bots, rather than just one, it's easier for the user to mentally divide the AI output into different "buckets".&lt;/p&gt;

&lt;p&gt;Also, because this is analagous to real human-human team-work, it's intuitive for people.&lt;/p&gt;

&lt;p&gt;Users can address individual bots by name, to ask for further assistance on a specific topic or aspect covered by just that bot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Financial advisor tool for recommending products to customers. Agents representing analysts and compliance each offer a perspective. The advisor uses these insights to prepare for a meeting with the client.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-multi-agent-chat.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-multi-agent-chat.svg" alt="Example: specialised multi-agent chat bots embedded in a financial advisor tool" width="342" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the wild:&lt;/strong&gt; Slack automation bots such as &lt;a href="https://trello.com/platforms/slack" rel="noopener noreferrer"&gt;Trello for Slack&lt;/a&gt;, &lt;a href="http://threadreaderapp.com" rel="noopener noreferrer"&gt;ThreadReaderApp&lt;/a&gt; on Twitter and dedicated chat platforms such as &lt;a href="http://threadreaderapp.com" rel="noopener noreferrer"&gt;Symphony&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeb651m4n6hhyj5ewgi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Foeb651m4n6hhyj5ewgi4.png" alt="In the wild: Trello for Slack automation bot" width="800" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgwxj3iyzncdvxo96yp6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqgwxj3iyzncdvxo96yp6.png" alt="In the wild: ThreadReaderApp on Twitter" width="799" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsttwaj48kph9qyyxn3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsttwaj48kph9qyyxn3q.png" alt="In the wild: Symphony dedicated multi-agent chat platform" width="800" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mimics:&lt;/strong&gt; 👭 Group of people working together, such as a team meeting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern: Fill-in-the-blanks
&lt;/h2&gt;

&lt;p&gt;A stencil is displayed, with some areas for user input and some ares for AI generated content.&lt;/p&gt;

&lt;p&gt;As the user fills in the inputs, the AI uses contextual information to generate more of the content. User and AI both work together until the full output has been generated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; Writing a CV for a job. You start to fill in work history items. The AI suggests additional bullet points, which you accept or refuse. The AI suggests shorter more focussed descriptions and word removal, which you accept or refuse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-fill-in-the-blanks.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-fill-in-the-blanks.svg" alt="Example: fill-in-the-blanks AI for CV editing tool" width="607" height="232"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mimics:&lt;/strong&gt; 📈 Collaborative white-boarding with colleagues (virtually or physically), collaborative card sorting exercises with a team.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern: Nudging controls
&lt;/h2&gt;

&lt;p&gt;A "work in progress" is displayed in the center while command-buttons for "nudging" are displayed around the edges or off to the side. By clicking the buttons, you can ask the AI to change the work along some dimension.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example:&lt;/strong&gt; 3D image manipulation. We ask the AI to make the shape more or less rounded, more or less flat, etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-nudging.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fconwy.co%2Fimages%2Farticles%2Fai-patterns%2Fai-ui-patterns-nudging.svg" alt="Example: nudging controls for AI-assisted logo editing tool" width="382" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the wild:&lt;/strong&gt; Dall-E image generator.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5qlb2c1riw3s8ue0xv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5qlb2c1riw3s8ue0xv7.png" alt="In the wild: Dall-E image generator" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mimics:&lt;/strong&gt; 💺 Pairing with a designer, where the designer is tweaking this or that based on your input.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://martinfowler.com/articles/exploring-gen-ai.html" rel="noopener noreferrer"&gt;&lt;em&gt;Exploring Generative AI&lt;/em&gt;&lt;/a&gt; • Birgitta BÖCKELER&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aima.cs.berkeley.edu/" rel="noopener noreferrer"&gt;&lt;em&gt;Artificial Intelligence: A Modern Approach&lt;/em&gt;&lt;/a&gt; • Stuart RUSSELL, Peter NORVIG&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.penguin.com.au/books/co-intelligence-9780753560778" rel="noopener noreferrer"&gt;&lt;em&gt;Co-Intelligence&lt;/em&gt;&lt;/a&gt; • Ethan MOLLICK&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://youtu.be/lxpASbe5Uys" rel="noopener noreferrer"&gt;&lt;em&gt;Living with AI Discombobulation&lt;/em&gt;&lt;/a&gt; • Clay SHIRKY&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>ui</category>
    </item>
  </channel>
</rss>
