<?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.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>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="800" 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 – 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;&lt;br&gt;
index.html&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;p&amp;gt;&lt;/span&gt;hello&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&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 – 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="137"&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="Image description" 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="241"&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="299"&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="478"&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; • 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://media.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://media.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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3s0a4v6rpftw5z7m2d8j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%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://media.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://media.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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6op6wkd8iny7z069prnw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6op6wkd8iny7z069prnw.png" alt="In the wild: " width="800" height="225"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsttwaj48kph9qyyxn3q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flsttwaj48kph9qyyxn3q.png" alt="In the wild: Symphony dedicated multi-agent chat platform" width="800" height="575"&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://media.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://media.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://media.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://media.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://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5qlb2c1riw3s8ue0xv7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu5qlb2c1riw3s8ue0xv7.png" alt="In the wild: Dall-E image generator" width="800" height="486"&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>
    <item>
      <title>Structured manual testing - benefits and techniques</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Fri, 13 Sep 2024 12:10:29 +0000</pubDate>
      <link>https://dev.to/conw_y/manual-testing-2ngk</link>
      <guid>https://dev.to/conw_y/manual-testing-2ngk</guid>
      <description>&lt;p&gt;While automated testing methods have been established for a long time in the software development process (e.g. unit, integration and end-to-end tests), relatively less attention has been paid to manual testing.&lt;/p&gt;

&lt;p&gt;However manual testing is far from "dead". Software developers still routinely verify their work by using products manually. Further, developers are usually required to take responsibility for the end-to-end functioning of their software, not just writing quality code and passing unit tests. They are usually encouraged not lean too heavily on QA teams. (In fact, some companies don't even have QA teams!) &lt;/p&gt;

&lt;p&gt;In this article, I will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Review the distinct benefits of manual testing.&lt;/li&gt;
&lt;li&gt;Present a real-life scenario where manual testing adds value.&lt;/li&gt;
&lt;li&gt;Provide guidance for structuring your manual testing, so you can get maximum benefit from it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benefits of manual testing
&lt;/h2&gt;

&lt;p&gt;Manual testing allows you to achieve certain specific goals which may not be available through automated testing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover &lt;strong&gt;&lt;em&gt;actual behaviour&lt;/em&gt;&lt;/strong&gt; – how the system &lt;strong&gt;&lt;em&gt;currently&lt;/em&gt;&lt;/strong&gt; behaves at runtime. (This information is not always readily available by other means.)&lt;/li&gt;
&lt;li&gt;Determine &lt;strong&gt;&lt;em&gt;intended behaviour&lt;/em&gt;&lt;/strong&gt; – how the system is &lt;strong&gt;&lt;em&gt;expected&lt;/em&gt;&lt;/strong&gt; to behave. (Also not always readily available other than by manually testing the system.)&lt;/li&gt;
&lt;li&gt;Perform testing &lt;strong&gt;&lt;em&gt;immediately&lt;/em&gt;&lt;/strong&gt; – without setting up test frameworks, etc.&lt;/li&gt;
&lt;li&gt;Perform testing in &lt;strong&gt;&lt;em&gt;any environment&lt;/em&gt;&lt;/strong&gt; you can access – not just local or QA environments.&lt;/li&gt;
&lt;li&gt;Put yourself &lt;strong&gt;&lt;em&gt;in the end-user's shoes&lt;/em&gt;&lt;/strong&gt; – experience the system as they would.&lt;/li&gt;
&lt;li&gt;Verify &lt;strong&gt;&lt;em&gt;complex, lengthy workflows&lt;/em&gt;&lt;/strong&gt; – which might be too difficult to automate. For example, complex interactions between an end-user and the system or complex data processing activities on the backend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As with any activity, manual testing can offer maximal benefit when performed in a structured manner.&lt;/p&gt;

&lt;p&gt;In my experience this involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Writing clear, structured test cases - e.g. heading, description, numbered steps with actions and expected results&lt;/li&gt;
&lt;li&gt;Organising test cases for rapid retrieval, by using consistent naming and tags&lt;/li&gt;
&lt;li&gt;Sharing test cases within the team/organisation, so others can leverage them&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Example of a test case
&lt;/h2&gt;

&lt;p&gt;Here's a simple example of a test case involving a user logging in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_can_login.md:

# User can login

Users who have an account should be able to log in.

## Steps

1. Go to the homepage
2. Click the login button
3. Expect that the login screen is shown
4. Enter username
5. Enter password
6. Click login screen submit button
7. Expect that you are shown logged in, in the header section
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice we have a brief heading and description, followed by neatly numbered steps.&lt;/p&gt;

&lt;p&gt;Steps can be:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Actions&lt;/em&gt;&lt;/strong&gt; (e.g. "click the login button") or&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Expectations&lt;/em&gt;&lt;/strong&gt; (e.g. "expect that the login screen is shown")&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This format allows us to quickly follow the steps of the test case (actions) and know what to look at to determine whether the test passed or failed (expectations).&lt;/p&gt;

&lt;h2&gt;
  
  
  Scenario: critical fixes for a startup
&lt;/h2&gt;

&lt;p&gt;A realistic scenario might make it easier for you to see how manual testing can help.&lt;/p&gt;

&lt;p&gt;Imagine you begin work as a software engineer at a rapidly growing startup, building a complex product with many user flows.&lt;/p&gt;

&lt;p&gt;You are assigned to work on the sign up experience. Users provide various personal details such as their country of residence. Based on these, the system provides various prompts then accepts payment.&lt;/p&gt;

&lt;p&gt;You are given your first development task:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Please fix the flow for Japanese customers. They are getting stuck at the point where they submit their personal details, but before they have paid for the product."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is based on direct customer contact. No one in the company can tell you exactly what "stuck" means or in exactly which part of the flow this is occurring.&lt;/p&gt;

&lt;p&gt;There is also minimal unit test code, code quality is not good and there's little documentation. Remember, it's a fast-growth startup – they don't have the same time and resources as a more mature company.&lt;/p&gt;

&lt;p&gt;How would you go about solving this? Your approach might look like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You go through the flow manually, simulating a Japanese customer (perhaps setting your browser location to Japan).&lt;/li&gt;
&lt;li&gt;As you go, you write down the steps you are taking, such as which details you entered, which buttons you clicked, etc. (This makes it easier to keep track of what you're doing, in case you need to restart the process). &lt;/li&gt;
&lt;li&gt;You find the exact point where the system is stuck - the submit button on screen 5 doesn't do anything.&lt;/li&gt;
&lt;li&gt;Examining the requests/responses, you discover that the system skipped the collection of the user's driver licence details, which are required for customers in certain countries, including Japan, causing an underlying API call to fail if not provided.&lt;/li&gt;
&lt;li&gt;Your verify this requirement with Backend engineers and the Product owner. Now you know what the fix is: you need to enable drivers licence details collection for Japanese customers.&lt;/li&gt;
&lt;li&gt;You make the fix in the relevant part of the code-base.&lt;/li&gt;
&lt;li&gt;Testing your work manually, you realise this data can be collected  earlier in the sign-up flow, with a skip option given for customers who don't have the details on hand. This will be a nicer user experience and increase the number of potential customers in the sales funnel.&lt;/li&gt;
&lt;li&gt;You complete all your changes, cover them with a unit test, save your manual testing steps in a markdown document (linked to from the pull-request) and push your changes.&lt;/li&gt;
&lt;li&gt;Once in Prod, you do a quick verification and see that everything works as expected. &lt;/li&gt;
&lt;li&gt;You can now report to the team that your task is completed with (hopefully) zero bugs!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice how documented manual testing helped you to solve this problem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You found the actual error by manually going through the flow (step 1).&lt;/li&gt;
&lt;li&gt;You kept track of your testing by writing down the steps, allowing you to quickly and efficiently repeat your test efforts whenever needed (steps 2, 7, 9).&lt;/li&gt;
&lt;li&gt;You easily verified your work in Prod (step 9).&lt;/li&gt;
&lt;li&gt;You empathised with the end-user and even found an opportunity to improve their experience as well as the onboarding rate (step 7).&lt;/li&gt;
&lt;li&gt;You added value to the team by documenting your manual testing steps (step 8).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As we'll soon see, this is only the beginning of the benefits! &lt;/p&gt;

&lt;h2&gt;
  
  
  Tagging your test cases
&lt;/h2&gt;

&lt;p&gt;Tagging can be a powerful way of making your whole test case collection searchable.&lt;/p&gt;

&lt;p&gt;Suppose every time you refer to the login screen in your Markdown files, you use the exact phrase: "login screen". Perhaps wrap it in brackets: "(login scren)".&lt;/p&gt;

&lt;p&gt;Now this exact phrase is searchable, via a simple find-in-files in your text editor. By searching for the string "(login screen)" you can find every test case involving that screen.&lt;/p&gt;

&lt;p&gt;For example, your search might yield the following results:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_can_login.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_can_recover_forgotten_password.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_cannot_login_with_wrong_credentials.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_can_login_from_another_country.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_can_login_with_a_linked_google_account.md&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This gives you powerful new capabilities such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Regression-testing&lt;/strong&gt; - checking various test cases, in case your change might have broken something.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exploratory-testing&lt;/strong&gt; - observing how the application behaves in various scenarios, generating ideas for improvement or uncovering hidden bugs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determining which unit tests to write&lt;/strong&gt; - to boost test coverage in a critical area of the application.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Test data
&lt;/h2&gt;

&lt;p&gt;Suppose a feature you want to test relies on certain data existing in the system beforehand.&lt;/p&gt;

&lt;p&gt;For example, you might need a certain kind of user account, such as a user who has their country set to Japan.&lt;/p&gt;

&lt;p&gt;You could create a test user in your testing environment - &lt;code&gt;hiroshi@yompail&lt;/code&gt; – and save it in your test case under a "Test data" heading.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_can_login.md:

# User can login

## Steps

1. Go to the homepage
...

## Test data

- User: hiroshi@yopmail.com / P@ssw0rd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results and artifacts
&lt;/h2&gt;

&lt;p&gt;It can be very useful to know the full list of dates/times when you ran your test and what the result was on each run.&lt;/p&gt;

&lt;p&gt;These can be added to a "runs" section of the test case file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;user_can_login.md:

# User can login

## Steps

1. Go to the homepage
...

## Runs

| Date/time               | Result    |
| ----------------------- | --------- |
| 2024-10-01 9:00 AM      | Succeeded |
| 2024-09-04 10:00 AM     | Failed    |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How might this be useful?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Spotting a pattern in failures&lt;/strong&gt; can indicate a systemic problem, such as insufficient compute resources or code quality issues with a particular part of the code base.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlating failures with code changes&lt;/strong&gt; can narrow your version control system search. For example, if you know the failure happened within the last week, you can limit your search changes made within that timeframe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When with manual testing, it is common for engineers to capture artifacts of their work, such as screenshots, screen recordings and copies of log output. These serve to demonstrate work done, prove that things worked correctly at a certain date/time and capture additional information that could help identify additional problems or improvement opportunities.&lt;/p&gt;

&lt;p&gt;Artifacts from manual tests can be organised alongside test cases, using a structured folder naming system.&lt;/p&gt;

&lt;p&gt;I have found it best to keep artifacts in folders named after the test cases and test run dates from which they were generated.&lt;/p&gt;

&lt;p&gt;Here's an example:&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;user_can_login.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;user_can_recover_forgotten_password.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;... etc&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;/test_artifacts&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;/user_can_login&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/2024_10_01_9_00_AM&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Screen Recording 2024-10-01 at 9.01.55 am.mov&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Untitled2.png&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;&lt;code&gt;/user_can_recover_forgotten_password&lt;/code&gt;&lt;/li&gt;

&lt;li&gt;... etc&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manual testing workflow
&lt;/h2&gt;

&lt;p&gt;You can make manual testing a regular, consistent part of your workflow. As you strengthen this habit, your work quality and overall knowledge of the system should improve.&lt;/p&gt;

&lt;p&gt;Here are some ideas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write a test case at the beginning of working on a major feature.&lt;/li&gt;
&lt;li&gt;Include or link to the test case in the task tracking system.&lt;/li&gt;
&lt;li&gt;Perform a test case of every major feature you deliver, writing a test case if one doesn't already exist.&lt;/li&gt;
&lt;li&gt;Include or link to your test case from every pull request you submit.&lt;/li&gt;
&lt;li&gt;Keep all your test cases in a shared knowledge system, such as your project's wiki.&lt;/li&gt;
&lt;li&gt;Copy relevant parts of a test cases in chat conversations about a feature or bug.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manual testing tools
&lt;/h2&gt;

&lt;p&gt;There are a range of software tools to help you write and manage test cases.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://dev.to/projects/testmatic"&gt;Testmatic&lt;/a&gt;. Shamless plug – I built this! It includes a web-based UI and CLI and saves everything to Markdown.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://azure.microsoft.com/en-us/products/devops/test-plans" rel="noopener noreferrer"&gt;Azure Test Plans&lt;/a&gt;. This one has a nice web-based UI and integrates with the Azure suite.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://does.qa/test-automation/codeless-vs-code" rel="noopener noreferrer"&gt;DoesQA&lt;/a&gt;. An interesting product that apparently allows you to write "codeless" but runnable tests.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Manual testing offers distinct and powerful benefits, not offered by automated testing, such as understanding and representing current and desired system behaviour, making fast progress in challenging environments with limited documentation and test coverage, verifying changes in multiple environments, verifying complex workflows and empathising with end-users.&lt;/p&gt;

&lt;p&gt;Structuring your manual test efforts compounds these benefits: you can quickly locate related tests (enabling regression and exploratory testing), ease your test efforts (using test data) and keep track of test results (helping you identify patterns in failures or find the root cause of an issue).&lt;/p&gt;

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

&lt;p&gt;These resources inspired this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.routledge.com/Software-Testing-A-Craftsmans-Approach-Fifth-Edition/Jorgensen-DeVries/p/book/9780367767624" rel="noopener noreferrer"&gt;&lt;em&gt;Software Testing - A Craftsman's Approach&lt;/em&gt;&lt;/a&gt; by Paul JORGENSEN&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>manual</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Streamlining code reviews</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Tue, 08 Aug 2023 04:58:00 +0000</pubDate>
      <link>https://dev.to/conw_y/streamlining-code-reviews-4016</link>
      <guid>https://dev.to/conw_y/streamlining-code-reviews-4016</guid>
      <description>&lt;p&gt;For myself and others, I've found that code reviews can be a challenging part of being a software developer.&lt;/p&gt;

&lt;p&gt;There may be many code changes to go through, many possible mistakes to be found and limited time to find and communicate them. Even before considering mistakes, the sheer amount and complexity of the code and changes can feel overwhelming – you don't know where to begin. It often feels like code review is chaotic and random, and it's down to luck whether or not it will be constructive.&lt;/p&gt;

&lt;p&gt;In this article I want to share my code review process, which I've evolved over time, in response to the pressures mentioned above.&lt;/p&gt;

&lt;p&gt;These are not necessarily based on hard evidence such as data and statistics, but more of a grab-bag of potentially useful ideas sampled from a range of different work environments, development stacks and teams, over many years.&lt;/p&gt;

&lt;p&gt;Broadly, there are three practices I follow, in order, for each code review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Context&lt;/em&gt;&lt;/strong&gt; – gain a high level understanding of the context surrounding the change, by checking the description, commit messages, task and any other documentation, and/or by asking the author&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Scan&lt;/em&gt;&lt;/strong&gt; – scan all of the code in the change, observe any questions or issues that come to mind, try to answer them on my own, otherwise leave a comment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Checklist&lt;/em&gt;&lt;/strong&gt; – make a final pass of all the code, this time against a checklist, and call out minor and major issues&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let me go into a bit more detail on how I perform each of these.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;It's difficult to meaningfully assess a code change without understanding its &lt;strong&gt;&lt;em&gt;context&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Context includes: what problem or requirement the code change intends to solve and how it fits into the broader context of the system.&lt;/p&gt;

&lt;p&gt;Without context, the code may either look superficially correct, leading you to approve it too quickly, or it may look very odd, generating innumerable questions to the author. Conversely, when you &lt;strong&gt;&lt;em&gt;do&lt;/em&gt;&lt;/strong&gt; understand the intention and context, a code change becomes much easier to understand and more intelligible.&lt;/p&gt;

&lt;p&gt;So I've found it useful to learn more about the context if I'm unclear.&lt;/p&gt;

&lt;p&gt;Here are some methods I use to learn more about the context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read descriptive notes and commit messages for the change, if any.&lt;/li&gt;
&lt;li&gt;Look up the task associated with the code change and read description and comments there.&lt;/li&gt;
&lt;li&gt;Look up code files related to the change and examine the code there.&lt;/li&gt;
&lt;li&gt;Look up the &lt;strong&gt;&lt;em&gt;history&lt;/em&gt;&lt;/strong&gt; of the files associated with the change, look up the tasks associated with that history, read the descriptions and comments.&lt;/li&gt;
&lt;li&gt;Search internal information sources (chat channels, wiki, etc.) using keywords found in the code.&lt;/li&gt;
&lt;li&gt;Ask the author of the change directly for context.&lt;/li&gt;
&lt;li&gt;Look up the authors of the files associated with the change in version control history and ask them directly for context.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the above might seem time-consuming, I've found it possible to fairly quickly improve my knowledge, even within minutes, by just picking a few of the relevant methods and applying them.&lt;/p&gt;

&lt;p&gt;For example, if I already work closely with the author involved, a simple message asking for context often gets a quick reply. Or if the code changes are attached to tasks, e.g. via task codes, it's usually possible to access the task with just a couple of clicks, and then read it within a few minutes.&lt;/p&gt;

&lt;p&gt;I believe this "context hunting" is usually worth the effort. It's not only about understanding the code change you're looking at. The benefits of contextual knowledge compound over time. Initially you might spend, say, 10 minutes reading and digesting contextual information, but eventually the time spend can approach 0, as you develop a systematic understanding of the whole system. That systematic understanding is highly valuable in all kinds of ways, not only for code review. It can help you to succeed in your own projects within the organisation and even help you to make the case for new projects and initiatives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scan
&lt;/h2&gt;

&lt;p&gt;After gaining context, the next step is to scan the code, getting a "big picture" view of how it hangs together.&lt;/p&gt;

&lt;p&gt;I'll often check out the change locally, open some of the files in my IDE, and use the navigation tools in the IDE to figure out what sequence of calls are being made, what data are being passed, etc. If it's a complex network of calls, I might spend a few minutes sketching an &lt;a href="https://dev.to/articles/visualising-execution-flows"&gt;execution flowchart&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I also survey the content of new code added, looking at key variables, control-flow structures (conditionals, switches, loops, etc), and getting an overall grasp of what the code does.&lt;/p&gt;

&lt;p&gt;At this point I may already have questions or issues for the author, and if so, I won't hesistate to leave a few comments.&lt;/p&gt;

&lt;p&gt;My comments will usually be in the following form:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Priority]: (Message)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This helps the author to understand my intent and prioritise which comments to reply to.&lt;/p&gt;

&lt;p&gt;For example, if it's a minor issue, which shouldn't necessarily block merging, I'll write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Minor]: `.forEach(expandSection)` might be more concise here.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But if it's a question, I'll write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Question]: Should this section be hidden for users without permissions?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if it's a major issue, I'll write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Major]: Should include an authorization check here.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Checklist
&lt;/h2&gt;

&lt;p&gt;By this point I'll have a pretty solid understanding of the change.&lt;/p&gt;

&lt;p&gt;Now it's a good time to run through a code review checklist and see if I missed anything significant.&lt;/p&gt;

&lt;p&gt;Because I broadly understand the change as a whole, even with a large checklist of 50 items, it's possible to quickly scan the checklist and pick out only the items that apply to the code.&lt;/p&gt;

&lt;p&gt;For example, suppose one of the items in my checklist is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query keys should be appropriately unique&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After scanning the change, I will already know whether or not the change includes any query keys at all. If it does not, then I can immediately skip this step.&lt;/p&gt;

&lt;p&gt;On the other hand, if the change &lt;strong&gt;&lt;em&gt;does&lt;/em&gt;&lt;/strong&gt; contain enums, then I will know to check the following item in my checklist:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enum values should match keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Where does this checklist come from?&lt;/p&gt;

&lt;p&gt;I usually build a unique checklist for each project I work on. As initial inputs to the checklist, I analyse the codebase I'm working on and read any technical documentation, such as coding standards.&lt;/p&gt;

&lt;p&gt;Subsequently I will add new items to the checklist, based on comments others leave on my change submissions, technical discussions with team members and general observations.&lt;/p&gt;

&lt;p&gt;Additionally, I've built up a pool of coding standards and best practices over my time as a developer. Some of these you can find documented in my article, &lt;a href="https://dev.to/articles/towards-zero-bugs"&gt;Towards zero bugs&lt;/a&gt;. I plan to publish a comprehensive list of them in a future blog post.&lt;/p&gt;

&lt;p&gt;This checklist isn't only useful for reviewing others' work – I use it on my own changes as well. By anticipating feedback and addressing it earlier, my code will already be of higher quality by the time it reaches the screens of others. This reduces the review workload on other engineers and improves my reputation within the team.&lt;/p&gt;

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

&lt;p&gt;Code reviews are an integral part of modern software engineering.&lt;/p&gt;

&lt;p&gt;At a team level, they're a great way to maximise code quality and ensure a shared understanding and knowledge of the code and systems.&lt;/p&gt;

&lt;p&gt;At an individual level, they're useful for understanding as much of the code as possible, both at a high level and a detailed level. This improves the quality of my own work and increases the likelihood of success in my current work and new initiatives within the organisation.&lt;/p&gt;

&lt;p&gt;Having a normalised process for performing code reviews helps make them easier and more fun. It also improves the quality of the feedback and, long-term, the code base.&lt;/p&gt;

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

&lt;p&gt;These books inspired this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.kobo.com/au/en/ebook/software-engineering-at-google"&gt;&lt;em&gt;Software Engineering at Google&lt;/em&gt;&lt;/a&gt; by Titus Winters, Tom Manshreck, Hyrum Wright&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>code</category>
      <category>reviews</category>
      <category>engineering</category>
    </item>
    <item>
      <title>Should we run unit tests in the cloud?</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sat, 17 Jun 2023 14:37:07 +0000</pubDate>
      <link>https://dev.to/conw_y/should-we-have-unit-tests-in-the-cloud-483i</link>
      <guid>https://dev.to/conw_y/should-we-have-unit-tests-in-the-cloud-483i</guid>
      <description>&lt;p&gt;Imagine if you could push your current branch to a test server and have it automatically run all of your unit tests!&lt;/p&gt;

&lt;p&gt;You could get feedback on your entire test suite in a few minutes, rather than waiting for a lengthier CI process, which might take up to ~20 minutes (depending on the infrastructure and setup in your organisation).&lt;/p&gt;

&lt;p&gt;Several automation test platforms now exist, to perform cloud testing on longer-running end-to-end tests. These include &lt;a href="https://www.selenium.dev/documentation/grid/"&gt;Selenium Grid&lt;/a&gt;, &lt;a href="https://testingbot.com/"&gt;TestingBot&lt;/a&gt; and &lt;a href="https://www.browserstack.com/"&gt;BrowserStack&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Why not extend this kind of service also to unit tests?&lt;/p&gt;

&lt;p&gt;Advantages of cloud-based unit test execution over CI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CI can be time-consuming, if it runs a full build, linting, type-checking and other operations. Cloud-based unit testing lets us isolate just the tests.&lt;/li&gt;
&lt;li&gt;CI tends to be monitored on a separate web-page. Cloud-based unit testing (in theory) could be integrated directly into the developer's IDE, so it would feel similar to running the tests locally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Advantages of cloud-based unit test execution over local:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When working with a very large code base, with a large number of tests, and not well de-coupled, we might want to run all the tests. An optimised cloud environment might be able to do this faster than a dev box, and without consuming resources which could cause our IDE and other tools to slow down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far I don't know of any platforms that actually do this, but perhaps the aforementioned automation testing platforms could be configured/tweaked to enable it.&lt;/p&gt;

&lt;p&gt;What do you think?&lt;/p&gt;

</description>
      <category>unit</category>
      <category>test</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Visualising execution flows</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sat, 10 Jun 2023 15:12:38 +0000</pubDate>
      <link>https://dev.to/conw_y/visualising-execution-flows-59e7</link>
      <guid>https://dev.to/conw_y/visualising-execution-flows-59e7</guid>
      <description>&lt;p&gt;Anyone who has spent some time developing software knows writing new code is but a small part of the job. At least as big, perhaps bigger, is &lt;strong&gt;understanding the existing code&lt;/strong&gt;. And that includes understanding the &lt;strong&gt;runtime behaviour&lt;/strong&gt; of that code!&lt;/p&gt;

&lt;p&gt;I often found myself having to understand a complex cluster of code modules, entailing many function calls being made and many data types being passed and returned.&lt;/p&gt;

&lt;p&gt;To properly understand the behaviour of the code, I needed to see a whole flow together at once so I could reason about it. I needed to somehow visualise it, e.g. by listing out the function calls in a text editor or maybe drawing a diagram on a piece of paper or in a diagramming application.&lt;/p&gt;

&lt;p&gt;After doing this quite a few times, I have started to evolve a more consistent and powerful format, one which is text-based (and so, easy to work on in a standard text editor) but can also be converted to a visual flowchart using a tool called Mermaid.&lt;/p&gt;

&lt;p&gt;In this article I want to describe this format and the reasoning behind it.&lt;/p&gt;

&lt;p&gt;Execution flow notations can be useful in understanding an existing code-base, troubleshooting bugs, communicating with other team members and for solution design.&lt;/p&gt;

&lt;p&gt;But first some background...&lt;/p&gt;

&lt;h2&gt;
  
  
  What do I mean by "execution flow"?
&lt;/h2&gt;

&lt;p&gt;It's helpful to define the concept of "execution flow".&lt;/p&gt;

&lt;p&gt;I'm referring to the path that the runtime will take through the code as it executes the code during a real-life use case.&lt;/p&gt;

&lt;p&gt;You should not confuse this with a more specific term: &lt;strong&gt;&lt;em&gt;call stack&lt;/em&gt;&lt;/strong&gt;. Since a flow can include multiple function calls in sequence, each producing its own distinct call stack, a flow can include multiple call stacks. Much of the complexity of an execution flow is precisely that calling of multiple functions and the passing of data to them and returning of data from them. So "call stack" is too narrow a term to cover what I'm trying to describe.&lt;/p&gt;

&lt;p&gt;On the other hand, you should also not confuse this with a more general term such as &lt;strong&gt;&lt;em&gt;abstract syntax tree&lt;/em&gt;&lt;/strong&gt; or "code structure". We are not describing the code as a whole, but just one path of possible execution of the code. Any piece of code that has one or more conditionals (e.g. &lt;code&gt;if&lt;/code&gt; or &lt;code&gt;switch&lt;/code&gt; statement, etc.) will execute differently depending on how those conditionals evaluate. For the same code, different lines might execute depending on the situation (e.g. depending on external state of some kind such as a database, web-service, system clock, etc.). Thus one code base can support multiple execution flows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example of an execution flow
&lt;/h2&gt;

&lt;p&gt;Let's use a hypothetical example – handling a user login on a Java backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LoginResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;   &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="mi"&gt;3&lt;/span&gt;   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt; &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;4&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userAuthProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isValidUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;       &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCurrentUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Login succeeded."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;     &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"User credentials are invalid."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;9&lt;/span&gt;     &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;10&lt;/span&gt;   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you spot the two execution flows in this code?&lt;/p&gt;

&lt;h3&gt;
  
  
  Flow 1 - Logging in successfully
&lt;/h3&gt;

&lt;p&gt;First, we have flow when the user's credentials are valid - that's lines 4-6:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;4&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userAuthProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isValidUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;5&lt;/span&gt;       &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCurrentUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="mi"&gt;6&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Login succeeded."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Inside &lt;code&gt;login()&lt;/code&gt;, the &lt;code&gt;if&lt;/code&gt; condition calls &lt;code&gt;UserAuthProvider::checkUserCredentials&lt;/code&gt;, passing user credentials.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UserAuthProvider::checkUserCredentials&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Execution proceeds into the &lt;code&gt;then&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;We call &lt;code&gt;SessionProvider::setCurrentUser&lt;/code&gt;, passing user credentials.&lt;/li&gt;
&lt;li&gt;We return &lt;code&gt;Response&lt;/code&gt;, passing success parameters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that this isn't just a single call-stack, as there are actually two method calls in this flow, each of which will generate its own call stack.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;UserAuthProvider::checkUserCredentials&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SessionProvider::setCurrentUser&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Flow 2 - Failure to log in
&lt;/h3&gt;

&lt;p&gt;What if the user credentials are &lt;em&gt;not&lt;/em&gt; valid and &lt;code&gt;isValidUser&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;That would be a separate execution flow - lines 4 and 7-8:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="mi"&gt;4&lt;/span&gt;     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;userAuthProvider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isValidUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="mi"&gt;7&lt;/span&gt;     &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="mi"&gt;8&lt;/span&gt;       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"User credentials are invalid."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Inside &lt;code&gt;login()&lt;/code&gt;, the &lt;code&gt;if&lt;/code&gt; condition calls &lt;code&gt;UserAuthProvider::checkUserCredentials&lt;/code&gt;, passing user credentials.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UserAuthProvider::checkUserCredentials&lt;/code&gt; returns &lt;code&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Execution proceeds into the &lt;code&gt;else&lt;/code&gt; block.&lt;/li&gt;
&lt;li&gt;We return &lt;code&gt;Response&lt;/code&gt;, passing failure parameters.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  IDE tools for determining execution flow
&lt;/h2&gt;

&lt;p&gt;So how to we figure out how our code flows in the first place?&lt;/p&gt;

&lt;p&gt;We can, of course, just read the code, open various files as needed, and try to follow along in our head.&lt;/p&gt;

&lt;p&gt;Thankfully we also have automated tools to help reduce some of the tedium. You'll likely be familiar with these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Go to definition&lt;/em&gt;&lt;/strong&gt; - we can select a reference (function, class, variable, etc) and be taken to its original definition&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;em&gt;Find references&lt;/em&gt;&lt;/strong&gt; - we can select a definition (function, class, variable, etc) and pull up a list of all points in the codebase which reference the definition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Different IDEs name these differently, but most mainstream IDEs have them in one form or another, including IntelliJ IDEA, VSCode, Visual Studio and xCode.&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%2Fiabhx4buqci8jgepfrmo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fiabhx4buqci8jgepfrmo.gif" width="760" height="570"&gt;&lt;/a&gt;&lt;br&gt;Screencast of a developer using Go to definition tool in IDEA
  &lt;/p&gt;

&lt;p&gt;For example, in the code sample given previously, we might use &lt;em&gt;Go to definition&lt;/em&gt; to locate the class whose &lt;code&gt;login&lt;/code&gt; method is being called.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;code&gt;LoginResource&lt;/code&gt; class and its &lt;code&gt;login&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Right-click the &lt;code&gt;isValidUser&lt;/code&gt; call and select "Go to definition".&lt;/li&gt;
&lt;li&gt;Observe that it is defined in the &lt;code&gt;UserAuthProvider&lt;/code&gt; class and its &lt;code&gt;isValidUser&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Go back the &lt;code&gt;LoginResource&lt;/code&gt; class and its &lt;code&gt;login&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Right-click the &lt;code&gt;setCurrentUser&lt;/code&gt; call and select "Go to definition".&lt;/li&gt;
&lt;li&gt;Observe that it is defined in the &lt;code&gt;SessionProvider&lt;/code&gt; class and its &lt;code&gt;setCurrentUser&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Go back the &lt;code&gt;LoginResource&lt;/code&gt; class and its &lt;code&gt;login&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Observe that a new &lt;code&gt;Response&lt;/code&gt; object is constructed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We might want to find out where else the &lt;code&gt;UserAuthProvider::isValidUser&lt;/code&gt; method is called.&lt;/p&gt;

&lt;p&gt;Supposing there was a &lt;code&gt;RegisterResource&lt;/code&gt; class having a &lt;code&gt;register&lt;/code&gt; method, as shown below:&lt;/p&gt;

&lt;pre&gt;
&lt;code&gt;
class RegisterResource {
  ...
  private Response register(String username, String password) {
    if (this.userAuthProvider.isValidUser(username, password)) {
      this.sessionProvider.setCurrentUser(username);
      return new Response(200, "Login succeeded.");
    } else {
      this.userAuthProvider.registerUser(username, password);
    }
  }
}
&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Then we might locate this piece of code by using the &lt;em&gt;Find references&lt;/em&gt; tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to the &lt;code&gt;UserAuthProvider&lt;/code&gt; class and its &lt;code&gt;isValidUser&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Right-click the &lt;code&gt;login&lt;/code&gt; method and select "Find usages".&lt;/li&gt;
&lt;li&gt;Observe that it is called in the &lt;code&gt;LoginResource&lt;/code&gt; class, in its &lt;code&gt;login&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Observe that it is also called in the &lt;code&gt;LoginResource&lt;/code&gt; class, in its &lt;code&gt;register&lt;/code&gt; method.&lt;/li&gt;
&lt;li&gt;Observe that a new &lt;code&gt;Response&lt;/code&gt; object is constructed.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Describing execution flows with text
&lt;/h2&gt;

&lt;p&gt;Suppose we wanted to make some notes of the execution flows we discovered. Maybe there are too many for us to easily memorise. Perhaps we want to see them all in one view rather than scattered among many files.&lt;/p&gt;

&lt;p&gt;Let's start with the first flow – successful login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LoginResource::login
  ---&amp;gt;|userName,password| UserAuthProvider::isValidUser
  ---|true| LoginResource::login

  ---&amp;gt;|userName,password| SessionProvider::setCurrentUser
  --- LoginResource::login

  ---&amp;gt;|200,'Login succeeded.'| Response::constructor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then the second flow – successful login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LoginResource::login

  ---&amp;gt;|userName,password| UserAuthProvider::isValidUser
  ---|false| LoginResource::login

  ---&amp;gt;|401,'Login failed. Invalid credentials.'| Response::constructor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the final flow – register:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;LoginResource::register
  ---&amp;gt;|userName,password| UserAuthProvider::isValidUser
  ---|true| LoginResource::register

  ---&amp;gt;|200,'Login succeeded.'| Response::constructor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can step back and look at all these flows together and see the bigger picture, e.g. how login and register both check user validity using &lt;code&gt;UserAuthProvider&lt;/code&gt;, and how both instantiate the Response class with various constructor parameters.&lt;/p&gt;




&lt;h4&gt;
  
  
  Aside: Sketching execution flows
&lt;/h4&gt;

&lt;p&gt;Observe that we don't have to cover the flows exhaustively, nor do we have to limit our coverage. We can cover just the parts of code that we are concerned with, based on our current goal, e.g. to solve the current bug or to learn more about a specific part of the code-base. We can make a kind of "partial sketch" of the parts of the execution flows that interest us.&lt;/p&gt;

&lt;p&gt;For example, we don't cover how &lt;code&gt;login&lt;/code&gt; and &lt;code&gt;register&lt;/code&gt; are called, and we don't cover which further calls are made by &lt;code&gt;UserAuthProvider&lt;/code&gt;, &lt;code&gt;SessionProvider&lt;/code&gt; or &lt;code&gt;Response&lt;/code&gt;, if those parts of the code don't interest us.&lt;/p&gt;

&lt;p&gt;And if we are dealing with a bug in which &lt;code&gt;isValidUser&lt;/code&gt; incorrectly returns &lt;code&gt;false&lt;/code&gt;, we can focus more on &lt;code&gt;isValidUser&lt;/code&gt; and which methods it calls.&lt;/p&gt;




&lt;h2&gt;
  
  
  Format for notating execution flows
&lt;/h2&gt;

&lt;p&gt;Did you notice the textual format used in the previous section to notate the execution flows?&lt;/p&gt;

&lt;p&gt;Let's deep-dive into that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class::methodCalling
  ---&amp;gt;|parameters| Class::methodBeingCalled
  ---|return values| Class::methodCalling
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Class::methodCalling&lt;/code&gt; - the caller&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;---&amp;gt;|parameters|&lt;/code&gt; - execution flowing from caller to callee, with the parameters being passed in the call&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Class::methodBeingCalled&lt;/code&gt; - the callee&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;---|return values|&lt;/code&gt; - execution flowing from callee back to caller, the value returned from the callee&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Class::methodCalling&lt;/code&gt; - the caller (again)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can chain these together to notate a sequence of consecutive calls.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Class1::method
  ---&amp;gt;|parameters| Class2::method
  ---&amp;gt;|parameters| Class3::method
  ---|return values| Class2::method
  ---|return values| Class1::method
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Representing closures and indirect calls
&lt;/h2&gt;

&lt;p&gt;Thusfar we've use the &lt;code&gt;Class::method&lt;/code&gt; format to reference the callers and callees. This should work reasonably well for classical OO code-bases written in Java, C#, Swift etc.&lt;/p&gt;

&lt;p&gt;But what if we want to reference code in other ways, such as named closures, for languages written in Javascript, Typescript, etc.?&lt;/p&gt;

&lt;p&gt;Here are some notations that could allow such structures to be referenced:&lt;/p&gt;

&lt;h3&gt;
  
  
  Nested closure
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;foo/bar&lt;/code&gt; - Reference a closure witin another closure.&lt;/p&gt;

&lt;p&gt;For example: &lt;code&gt;retry/handleTimeout&lt;/code&gt; references a &lt;code&gt;handleTimeout&lt;/code&gt; function nested inside a &lt;code&gt;retry&lt;/code&gt; function in Javascript.&lt;/p&gt;

&lt;h3&gt;
  
  
  Indirect call
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;-.-&amp;gt;&lt;/code&gt; - References an indirect method call - a call which our code doesn't make directly, but causes to be made, such as calling &lt;code&gt;setTimeout&lt;/code&gt; on a function in Javascript. It looks like a dotted line.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example in Javascript
&lt;/h3&gt;

&lt;p&gt;Let's use an example – a recursive Javascript function – to put all these ideas together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleTimeout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;action&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;times&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;timeout&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;&lt;code&gt;retry&lt;/code&gt; is a recursive function, which calls &lt;code&gt;setTimeout&lt;/code&gt;, passing a closure. That closure executes. Depending on the number of times &lt;code&gt;retry&lt;/code&gt; has called itself already (&lt;code&gt;time&lt;/code&gt;), it may call &lt;code&gt;retry&lt;/code&gt; again or simply do nothing, halting the recursion.&lt;/p&gt;

&lt;p&gt;We can notate this execution flow, including the closure, using the nested closure, multiple calls, row/column and indirect call notations given above, in the following manner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;retry
  -.-&amp;gt;|action, times=3| retry/handleTimeout
  ---&amp;gt; action
  ---|false| retry/handleTimeout
  ---&amp;gt;|action, times=3, 2| retry
  -.-&amp;gt;|action, times=3, count=2| retry/handleTimeout
  ---&amp;gt; action
  ---|true| retry/handleTimeout
  ---retry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Visualising flows with Mermaid
&lt;/h2&gt;

&lt;p&gt;Now the juicy part – lets look at how this format can be instantly converted into a visual flowchat using Mermaid!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.js.org/" rel="noopener noreferrer"&gt;Mermaid&lt;/a&gt; is a free, open-source tool, which takes code written in a specific syntax and converts it into a diagram.&lt;/p&gt;

&lt;p&gt;You can run Mermaid in the browser using &lt;a href="https://mermaid.live/" rel="noopener noreferrer"&gt;Mermaid Live&lt;/a&gt;, or if you prefer, you can download and run it locally using the instructions on the &lt;a href="https://github.com/mermaid-js/mermaid-live-editor" rel="noopener noreferrer"&gt;mermaid-live-editor&lt;/a&gt; GitHub profile.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; We'll need to add the keyword &lt;code&gt;graph&lt;/code&gt; to the top of the text.&lt;/p&gt;

&lt;p&gt;Also, in these examples, we add numbered circular nodes (e.g. &lt;code&gt;---n1((1))&lt;/code&gt;) to indicate the order of execution.&lt;/p&gt;




&lt;p&gt;The following is how our two earlier Java examples – login success and login failure – render in Mermaid:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNqNkU9LAzEQxb_KMpdOYVfa7Z9DDoKoNxGx6CmXkEy7gd1kySSKtP3uZqWuPdWeknl5837DZA_aGwIBu6D6Rronv7PuldinoEmIdiilK4qqqtwccT6dnqrbQ2IKz6qjslfMnz6YQ_GWpbsUm5fgP6yhIITld9VaMzz8xtSI9RhzibdAXFzmbYjZeveHY4r3KQRy8Zy4RFxeRVwhrs6I9WxWTn78BSetiQyZm8mhyN29d5y7dT5iSDr6EbZGXP8DgxI6Cp2yJi9-P1glxIY6kiDy1dBWpTZKkO6YrSpFv_lyGkQmUQmpNyrSg1X5yzoQW9XyqD4am0c5icdvzQeeBg" rel="noopener noreferrer"&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fw76kaav3mbpqji8gtgnu.png" width="800" height="333"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;Login success flow example rendered by Mermaid
  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNp9kE9rwzAMxb-K0aUupGVpc_JhMNgOgzHGxnbyRdhKY0js4D8do-l3n1La0sPYydbz8-9JOoAJlkDBLuLYaf8Sds6_UwolGlKqn0vthVitVr6Wsl4uz9X9VBLFVxyoGjGl7xDtJD5Zeii5e4th7yxFpVz6wt7Z-eGC2Ui5ucG02CeaxH_BWym3Nz-au7panPyiRdeTXYtnv59jhIlkyWfHzPViEswbg0_MM3zkWEwO1z4aKZsr9e94qGCgOKCzvKHDbNWQOxpIg-KrpRZLnzVof2Qrlhw-frwBxUlUQRktZnp0yLsdQJ0GvahP1nErZ_H4C48Egj8" rel="noopener noreferrer"&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Famsl66c6qfd35w32nzcn.png" width="606" height="350"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;Login failure flow example rendered by Mermaid
  &lt;/p&gt;

&lt;p&gt;And here's the Javascript example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNp9Uc1qwzAMfhWjkwrxRpP-EWhP2xNsR19MrC6G2C6ufChN331ulrQwaG7W9yN9kq_QBENQw0_Up1b5SBwvygshpfRLxOViMVRv8tDrhm3whWDr6LyvejGI31vtTUffGQyJJ2uJWI5WKQ_izzqRFWL1IPuj7s4022yFuHo2-5-jEOXonvRrxPXr3IVoQvK8L2dnbhA3LxfYIm6fC3BM8_l3iLuHfNBBAY6i09bk01_vjAJuyZGCOj8NHXXqWIHytyzVicPXxTdQ30cVkE5GM31YnT_NQT0ccEI_jeUQR_D2C9J_lug" rel="noopener noreferrer"&gt;&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.amazonaws.com%2Fuploads%2Farticles%2Fb64divexjmohrg3ew5nw.png" width="642" height="526"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;Javascript recursive function example rendered by Mermaid
  &lt;/p&gt;

&lt;p&gt;Notice that we've added small numbered circles, indicating the order in which the calls occur. This makes the flow a bit easier to navigate.&lt;/p&gt;

&lt;p&gt;Imagine this appearing in a Slack conversation.&lt;/p&gt;

&lt;p&gt;It could potentially be easier to read and follow an execution flow diagram than to read paragraphs of text trying to describe in plain language the complex sequence of calls.&lt;/p&gt;




&lt;h4&gt;
  
  
  Aside: Asynchronicity and concurrency
&lt;/h4&gt;

&lt;p&gt;Though we touched on async in the Javascript example with the &lt;code&gt;setTimeout&lt;/code&gt; call, we haven't fully addressed the issue of describing asynchronicity or concurrency in execution flows.&lt;/p&gt;

&lt;p&gt;This is probably a fairly deep topic that deserves a dedicated article. However I have no doubt it can be represented diagrammatically, as long as a strict convention is adhered to.&lt;/p&gt;

&lt;h2&gt;
  
  
  Isn't this just a flowchart?
&lt;/h2&gt;

&lt;p&gt;Yes, but it's a &lt;strong&gt;&lt;em&gt;specialised&lt;/em&gt;&lt;/strong&gt; form of flowchart, focussed on representing execution flow.&lt;/p&gt;

&lt;p&gt;The flowchart directly maps to the code it represents, so it accurately and unambiguously conveys information about that code. At the same time, because it's not actually code, but a diagram, it allows us to more easily view and reason about the code in terms of execution flows specifically. We don't have to jump around between files, scroll up and down, etc. but can see a whole execution flow in one screen.&lt;/p&gt;

&lt;p&gt;Also by establishing and adhering to a convention in how we represent callers, callees, parameters and return values, etc. this flowchart technique is re-usable across programming languages, codebases, business domains, etc. A similar versatility is found in UML, sequence diagrams and other kinds of specialised diagram formats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use sequence diagrams?
&lt;/h2&gt;

&lt;p&gt;You might have seen diagrams similar to those described here, but laid out as sequence diagrams. Execution flows can certainly be visualised as sequence diagrams. A sequence diagram is arranged as a set of vertical columns connected by arrows, where each column represents a method and each arrow represents a call.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.live/edit#pako:eNqdkUFLAzEQhf_KMud1KR5zKIh68yAtesplSKZtYDdZZzKKlP53J9YWBNmDOYTJ4-XL4-UIoUQCBz4LvSnlQA8J94yTz52tp7JPeUNSlAMNYzvdrNcvQnyn9fDM5T1F4iHJK44pNt11anvGifoZRT4KxzNq6ZIx_3jJdZWVfF6KsiWRVPIVKlTvlZly_R3mzFh2_z-FCXOxBofL4Lrb1ar38G3vREMgihQHb0VDDxPxhCla78eG9VAPZBnB2RhphzrW5jyZFbWW7WcO4FqMHnSOWC-fBG6Ho1zVx5hq4R_x9AXa0Ku1" rel="noopener noreferrer"&gt;&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.amazonaws.com%2Fuploads%2Farticles%2F1k0fs7azpu0uh0qbhb8d.png" alt="" width="800" height="317"&gt;&lt;br&gt;
  &lt;/a&gt;&lt;br&gt;Login success flow example as a sequence diagram rendered by Mermaid 
  &lt;/p&gt;

&lt;p&gt;There are weaknesses of sequence diagrams, however.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They present each method in a column, so we may soon run out of horizontal space, whereas flowcharts can flow &lt;strong&gt;&lt;em&gt;down and across&lt;/em&gt;&lt;/strong&gt;. Also, even for lengthy flowcharts, scrolling up and down is easier on most devices than scrolling side-ways.&lt;/li&gt;
&lt;li&gt;They may position the caller and the callee very far apart, so that the eye has to scan back and forth over a large distance to see the call, whereas flowcharts can more position the caller and callee closer together, making scanning easier.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For these reasons, I find the flowchart format more appealing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Are there tools that generate execution flowcharts automatically?
&lt;/h2&gt;

&lt;p&gt;Surprisingly, not really.&lt;/p&gt;

&lt;p&gt;For dependency visualisation, I found a &lt;a href="https://marketplace.visualstudio.com/items?itemName=sz-p.dependencygraph" rel="noopener noreferrer"&gt;few&lt;/a&gt; &lt;a href="https://marketplace.visualstudio.com/items?itemName=lilinhao.vscode-pylonn" rel="noopener noreferrer"&gt;interesting&lt;/a&gt; &lt;a href="https://marketplace.visualstudio.com/items?itemName=CodeLogic.vscodecape" rel="noopener noreferrer"&gt;plugins&lt;/a&gt; for VSCode, and also experimented with &lt;a href="https://www.jetbrains.com/help/idea/dependencies-analysis.html" rel="noopener noreferrer"&gt;IDEA's dependencies analysis&lt;/a&gt; tool.&lt;/p&gt;

&lt;p&gt;However, all of these tools are focused on reporting &lt;strong&gt;&lt;em&gt;compile-time dependencies&lt;/em&gt;&lt;/strong&gt;, which are a different kind of thing to &lt;strong&gt;&lt;em&gt;execution flows&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Dependency graphs of course help us to understand how code is structured, but they don't give us the full picture of which parts of that code execute in which order at runtime. For that, we really need execution flows.&lt;/p&gt;

&lt;p&gt;Theoretically any tool that could automatically report execution flows would need to be able to analyse the code in terms of its expected execution at runtime. The tool might, like a debugger, execute the code, in order to determine the flow of control, e.g. where the flow of control depends on some state which can only be discovered at runtime. Or it could statically analyse the code to determine all possible flows and generate a report of all of them.&lt;/p&gt;

&lt;p&gt;It's beyond the scope of this article to look into how such a tool could be developed, but it's something I'm interested in looking into and perhaps even undertaking myself.&lt;/p&gt;




&lt;h3&gt;
  
  
  Update on ChatGPT (11/06/2023)
&lt;/h3&gt;

&lt;p&gt;Experimentation with &lt;a href="https://chat.openai.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; yielded promising results.&lt;/p&gt;

&lt;p&gt;The LLM (Large Language Model) tool was able to generate &lt;a href="https://chat.openai.com/share/63de0d0e-3e97-48f6-aceb-e7e848e56492" rel="noopener noreferrer"&gt;a flowchart with labels in plain-English in both ASCII and Mermaid formats&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The flowchard did accurately follow the flow of the code. However, it did not use the format I described above, which is intended to directly map to elements in the code (function names, variable names, etc).&lt;/p&gt;

&lt;p&gt;With some more training of ChatGPT, more detailed prompts or a more customised LLM tool than ChatGPT, perhaps it will be possible in the near future for a chat-bot to generate execution flows automatically. That would be cool!&lt;/p&gt;

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

&lt;p&gt;This article has outlined a format for describing execution flow, which can be used to visualise and understand how parts of a codebase execute at runtime (and generate diagrams).&lt;/p&gt;

&lt;p&gt;This understanding can help to diagnose bugs/errors, determine the best points at which to change the code, estimate how long changes might take, and no doubt many other use cases.&lt;/p&gt;

&lt;p&gt;I hope you find it useful!&lt;/p&gt;

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

&lt;p&gt;These books inspired this article:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;The pocket guide to debugging&lt;/em&gt; • Julia EVANS&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;UML distilled&lt;/em&gt; • Martin FOWLER&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flowchart</category>
      <category>execution</category>
      <category>visualising</category>
      <category>diagrams</category>
    </item>
    <item>
      <title>Increase your test coverage with combinatorial testing</title>
      <dc:creator>Jonathan</dc:creator>
      <pubDate>Sat, 15 Oct 2022 09:24:07 +0000</pubDate>
      <link>https://dev.to/conw_y/increase-your-test-coverage-with-combinatorial-testing-ak2</link>
      <guid>https://dev.to/conw_y/increase-your-test-coverage-with-combinatorial-testing-ak2</guid>
      <description>&lt;p&gt;Have you recently tried to unit test a function which has many combinations of possible inputs and expected outputs?&lt;/p&gt;

&lt;p&gt;An increasingly common way of writing such a test is to utilize a data-driven test. The problem with data-driven tests is that they can quickly grow to be large and unwieldy.&lt;/p&gt;

&lt;p&gt;In this article, I want to introduce a technique for generating data-driven tests without having to spell out every individual combination of inputs/outputs in code.&lt;/p&gt;

&lt;p&gt;But first, a quick refresher on data-driven tests...&lt;/p&gt;

&lt;h2&gt;
  
  
  Data-driven tests and their limitations
&lt;/h2&gt;

&lt;p&gt;You may be familiar with &lt;a href="https://en.wikipedia.org/wiki/Data-driven_testing"&gt;data-driven testing&lt;/a&gt;. Basically you write a table of combinations of inputs and outputs in which each test case is inputted as "row" of data. Data-driven testing is now supported by many popular test frameworks (Jest, JUnit, NUnit to name a few).&lt;/p&gt;

&lt;p&gt;One problem with data-driven testing is: sometimes we have so many combinations to cover that a comprehensive data-driven test would be very lengthy and difficult to read or maintain.&lt;/p&gt;

&lt;p&gt;Imagine, for example, trying to write a data-driven unit test for a function which returns the &lt;strong&gt;number of days in a given month&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The function takes two parameters: &lt;code&gt;year&lt;/code&gt; and &lt;code&gt;month&lt;/code&gt; and returns one &lt;code&gt;day&lt;/code&gt; value. It has to deal with a range of values for each parameter. Multiplying all values that need to be tested by two parameters yields a large number of combinations.&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="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jan&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;feb&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;march&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;april&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

  &lt;span class="c1"&gt;// and so on and so on ... 😓&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That number of combinations, though easy for a computer to process, is not easy for us to wrap our human minds around!&lt;/p&gt;

&lt;p&gt;Perhaps the solution is to express the combinations in a more concise manner – as grouped ranges of values – rather than spelling out every single combination.&lt;/p&gt;

&lt;p&gt;Combinator function to the rescue!&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a combinator?
&lt;/h2&gt;

&lt;p&gt;In the world of functional programming, the term "combinator" informally refers to a pattern...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"where complex structures are built by defining a small set of very simple 'primitives', and a set of 'combinators' for combining them into more complicated structures"&lt;br&gt;&lt;br&gt;
– &lt;em&gt;Combinator Pattern&lt;/em&gt; • &lt;a href="https://wiki.haskell.org/Combinator_pattern"&gt;wiki.haskell.org/Combinator_pattern&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The combinator I present in this article is more specific. It takes as input an object whose properties each have a value that is an array. Then it combines each value of each array. All of the objects generated by this means are then returned to the caller.&lt;/p&gt;

&lt;p&gt;For example, suppose we provide an input object having a single property, "color", whose value is an array containing elements "red" and "blue":&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The combinator will return us an array having the following objects:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an object having a "color" property whose value is "red" and&lt;/li&gt;
&lt;li&gt;an object having a "color" property whose value is "blue"
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suppose we provide an additional property in our input object, "brightness", whose value is an array containing elements 100 and 200:&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="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;brightness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The combinator will return us an array having every specified combination of "color" and "brightness":&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;an object having a "color" property whose value is "red" and a property "brightness" whose value is 100 and&lt;/li&gt;
&lt;li&gt;an object having a "color" property whose value is "red" and a property "brightness" whose value is 200 and&lt;/li&gt;
&lt;li&gt;an object having a "color" property whose value is "blue" and a property "brightness" whose value is 100 and&lt;/li&gt;
&lt;li&gt;an object having a "color" property whose value is "blue" and a property "brightness" whose value is 200&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;brightness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;red&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;brightness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;brightness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&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="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;blue&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;brightness&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&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;Providing a definition object as input, we can get a large set of results as output.&lt;/p&gt;

&lt;p&gt;Here's a high-level diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mermaid.ink/img/pako:eNp1kk1ugzAQha9izSqoEVJYWlGkSmkvUHZ1Fy4eWqtgo8GuhBCcveYnTknTlfG8z483Y_dQWIXAoahk2561_CBZC8PYvGdnLLXRTlsz5iPrJ4Gx4zHvGjydlt2uIdsgue6QcPZIJLvRBdmWLH-N0tt4A2f_w1mE0zT9i4XiAgxzTFu_ayMdsjQ9beMKc-0jYps2nr0pJvjSyoOK5_mt193_LdGCJWeEzpNpJyZWJyT_HSMq_Wp4lfJLrM08d9-y8nhI7kxvkbJkM6t5Wb1hDzVSLbUK1zubC3CfWKMAHj6VpC8BwgyBk97Zl84UwB153INvVGhxfQ3AS1m1sfqktLO0FocfpVfATw"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwn0t6pah0i2qqqi535sw.jpeg" alt="UML high-level diagram showing how a combinator function is designed" width="495" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's apply this combinator to a slightly more "real world" example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Applying a combinator to an example: days in a month
&lt;/h2&gt;

&lt;p&gt;For historical reasons, determining the number of days in a month in the Western calendar is complicated.&lt;/p&gt;

&lt;p&gt;The following short rhyme tries to summarize the rules in a memorable way:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thirty days have September,&lt;/p&gt;

&lt;p&gt;April, June and November.&lt;/p&gt;

&lt;p&gt;All the rest have thirty-one,&lt;/p&gt;

&lt;p&gt;except February alone, which has&lt;/p&gt;

&lt;p&gt;twenty-eight days each year&lt;/p&gt;

&lt;p&gt;and twenty-nine days each leap-year&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Suppose we wanted to unit-test a function, &lt;code&gt;getDaysInMonth&lt;/code&gt;, which takes &lt;code&gt;month&lt;/code&gt; and &lt;code&gt;year&lt;/code&gt; as input and returns a number of &lt;code&gt;days&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We could simply input every possible date into the unit test and assert on the month of each. As mentioned above, that could involve quite a lot of fiddling in Excel and would result in a very long and not very human-readable test file.&lt;/p&gt;

&lt;p&gt;Instead, let's try to tackle this problem with a combinator.&lt;/p&gt;

&lt;p&gt;Starting with the first two lines of the rhyme:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Thirty days have September,&lt;/p&gt;

&lt;p&gt;April, June and November.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We can express this "thirty days" combination set programmatically, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thirtyDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;april&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;june&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;september&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;november&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result can easily be passed into a data-driven test in Jest:&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="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thirtyDays&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$month in $year should have $expectedDays days&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="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedDays&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="nf"&gt;getDaysInMonth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedDays&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;On running the unit test, the following test cases will be generated and executed:&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="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;april&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;june&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;september&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;november&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;april&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;june&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;september&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;november&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;april&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;june&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;september&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;november&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;april&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;june&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;september&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;november&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how we can use a small amount of code (in this example, 5 lines for the &lt;code&gt;combinate&lt;/code&gt; call) to generate a much larger set of test cases (16). This gives our test code more leverage.&lt;/p&gt;

&lt;p&gt;Covering the remaining lines of the rhyme:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;All the rest have thirty-one,&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;thirtyOneDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;january&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;march&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;may&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;july&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;august&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;october&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;december&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following data will be generated:&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="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;january&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;march&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;may&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;july&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;august&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;october&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;31&lt;/span&gt; &lt;span class="nx"&gt;days&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="nx"&gt;etc&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;except February alone, which has&lt;br&gt;
twenty-eight days each year&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;februaryDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;february&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;28&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;february&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;and twenty-nine days each leap-year&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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;februaryLeapYearDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;february&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;29&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="err"&gt;✓&lt;/span&gt; &lt;span class="nx"&gt;february&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;2024&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;have&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="nf"&gt;days &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, putting it all together, here is the complete unit test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;getDaysInMonth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thirtyDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;april&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;june&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;september&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;november&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;30&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;thirtyOneDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2020&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;january&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;march&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;may&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;july&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;august&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;october&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;december&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;31&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;twentyEightDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2023&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;february&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;28&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;twentyNineDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;combinate&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2024&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;february&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;expectedDays&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;thirtyDays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;thirtyOneDays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;twentyEightDays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;twentyNineDays&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;])(&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;$month in $year should have $expectedDays days&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="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expectedDays&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="nf"&gt;getDaysInMonth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expectedDays&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;Notice that we can assign meaningful names to each of the variables, increasing the readability of the test code.&lt;/p&gt;

&lt;p&gt;I'm sure you would agree that this test code, using a combinator, is more concise and readable than a large table of numbers and strings!&lt;/p&gt;

&lt;p&gt;In closing, I encourage you to use combinatorial testing to shorten and sweeten your data-driven tests, thus testing your software thoroughly and making it maximally robust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing combinator-util
&lt;/h2&gt;

&lt;p&gt;If you'd like to add a little combinatorial goodness to your unit tests, please check out this re-usable, open-source NPM package:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jonathanconway/combinator"&gt;https://github.com/jonathanconway/combinator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Contributions welcome!&lt;/p&gt;

</description>
      <category>unittesting</category>
    </item>
  </channel>
</rss>
