<?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: Uilicious</title>
    <description>The latest articles on DEV Community by Uilicious (@uilicious).</description>
    <link>https://dev.to/uilicious</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%2Forganization%2Fprofile_image%2F182%2F2f087e13-6b3f-4089-b781-a01543a38062.png</url>
      <title>DEV Community: Uilicious</title>
      <link>https://dev.to/uilicious</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/uilicious"/>
    <language>en</language>
    <item>
      <title>SAINE is Mathematically One of the Best WORDLE Starting Words - Here's Why</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Fri, 15 Apr 2022 22:52:57 +0000</pubDate>
      <link>https://dev.to/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396</link>
      <guid>https://dev.to/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396</guid>
      <description>&lt;p&gt;TLDR: I wrote a Wordle solver bot with Javascript and UIlicious. You can run &lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;this snippet&lt;/a&gt; any day to get your daily Wordle solution. Try and see if you can get a better score than the bot! &lt;strong&gt;Feel free to edit it and optimise the solver algorithm!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The wordler solver is covered in 3 parts, which covers the&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI interaction code &lt;a href="https://uilicious.com/blog/automate-wordle-via-uilicious/" rel="noopener noreferrer"&gt;(link here)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Wordle Statistical model, and the math behind it (this article) &lt;/li&gt;
&lt;li&gt;Unit testing and benchmarking of the wordle solver (@ TODO) &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All the statistical examples can be found at the following link:  &lt;a href="https://uilicio.us/wordle-statistics-sample" rel="noopener noreferrer"&gt;https://uilicio.us/wordle-statistics-sample&lt;/a&gt;&lt;br&gt;
And is generated via code used here: &lt;a href="https://github.com/uilicious/wordle-solver-and-tester" rel="noopener noreferrer"&gt;https://github.com/uilicious/wordle-solver-and-tester&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&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%2Fic5us2bzeu7idt659wm8.gif" 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%2Fic5us2bzeu7idt659wm8.gif" alt="Cat reading the art of war"&gt;&lt;/a&gt;&lt;/p&gt;
Cat planning our Wordle strategy



&lt;h1&gt;
  
  
  Our WORDLE strategy
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer, I do not claim for this to be the best WORDLE strategy (yet), But it is a rather good one =)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Before going into the statistics, let's first cover our WORLDE strategy.&lt;/p&gt;

&lt;p&gt;One thing that humans do really well that classic computers are bad at is to "intuitively" figure things out. Unless I plan to train a neural network, the computer program I am developing will need to use a classic dictionary word list to make its guesses.&lt;/p&gt;

&lt;p&gt;The one thing that computers do well however, is memorising giant lists of words or data. And performing math on it. So let's use this to our advantage by doing the following sequence.&lt;/p&gt;

&lt;p&gt;Given the 2 word list, one full of possible answers ( ~2.3k words), and another with the full word list (13k words)...&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filter out words in the possible answer list against the current game state from past guesses.&lt;/li&gt;
&lt;li&gt;Count the number of times each character appears in the answer word list in their respective word positions.&lt;/li&gt;
&lt;li&gt;From the full word list, choose the word which is most likely to find a correct character guess. Independently score it preferring words that give more information in the first 4 rounds with either an exact or partial match.&lt;/li&gt;
&lt;li&gt;Choose the highest scoring word and try it.&lt;/li&gt;
&lt;li&gt;Repeat from the top if needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Also to clarify:&lt;/strong&gt; We do not memorise the previous Wordle solutions (I felt that was cheating as the system could end up just memorising the day-to-day list in sequence).&lt;/p&gt;

&lt;p&gt;While the exact details of the scoring change slightly depending on the round - to optimise things. It does not change the overall concept on a high level.&lt;/p&gt;

&lt;p&gt;So how does this work in practice? For our current iteration of the Wordle strategy, we will view this in practice step by step (without code).&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%2F166vkmh4ewjt5bz5ygyw.gif" 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%2F166vkmh4ewjt5bz5ygyw.gif" alt="Man lost in counting"&gt;&lt;/a&gt;&lt;/p&gt;
Counting statistics is "easy"



&lt;h1&gt;
  
  
  Our starting word: SAINE - and why you should use it
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;An alias to sain, meaning: to make the sign of the cross over (oneself) so as to bless or protect from evil or sin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There has been lots of advice on the starting 2 words. And it makes sense that this word follows with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;3 very common vowels&lt;/li&gt;
&lt;li&gt;All unique characters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But let's figure out why the solver chooses this word by looking at the numbers.&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%2Fvinckh9c9imnh4udoi33.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%2Fvinckh9c9imnh4udoi33.png" alt="Opening word stats"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the characters in Wordle.



&lt;p&gt;Based on the statistics. "SAINE" has the highest chance of finding an exact green character match as an opening word while having 3 of the top vowels.&lt;/p&gt;

&lt;p&gt;Reading the raw distributing table can be understandably hard to digest. So let me recontextualize those numbers here. SAINE has a …&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;91.06% chance of partial matching at least 1 character (yellow/green);&lt;/li&gt;
&lt;li&gt;55.94% chance of partial matching at least 2 characters (yellow/green);&lt;/li&gt;
&lt;li&gt;50.24% chance of exactly matching at least 1 character (green).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is a  really high chance of getting at least one or 2 major clues. Inversely, because there are few words without A, I, and E, failure to match is a "huge clue."&lt;/p&gt;

&lt;p&gt;Not bad for an opening huh?&lt;/p&gt;

&lt;p&gt;What about other popular opening words such as "CRANE" and "ADEPT"?&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%2Fxfaerpv8y7nplwynqv9k.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%2Fxfaerpv8y7nplwynqv9k.png" alt="Probability distribution table for different words"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of first word guess for SAINE, CRANE and ADEPT.



&lt;p&gt;The only key advantage, for "CRANE / ADEPT" is they both have a 0.04% chance of successfully doing a 1-word guess. I suspect the flaw of the previous public analysis was how they were limiting the opening word to the known answer list. I believe however, we should be using the full word list instead, to maximize the probabilities of clues in favor of a very narrow chance of doing a 1-word guess.&lt;/p&gt;

&lt;p&gt;More importantly, SAINE has a significantly higher chance (by  ~7%) of guessing an exact match (green) on the first try. Which is incredibly helpful as a clue.&lt;/p&gt;

&lt;p&gt;With the starting word debate out of the way, we will see how the system reacts with the different results!&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%2Fnrrjh4uwgz2hrdx96mv9.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%2Fnrrjh4uwgz2hrdx96mv9.png" alt="2 wordle result, side by side"&gt;&lt;/a&gt;&lt;/p&gt;
Wordle results before and after migrating to the NYTimes.



&lt;h2&gt;
  
  
  Understanding how the 2nd guess PLUTO, leads to PAUSE
&lt;/h2&gt;

&lt;p&gt;So let us look at how the 2nd word is chosen for the answer "PAUSE" (left-hand side).&lt;/p&gt;

&lt;p&gt;We are given the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Letter I &amp;amp; N is not in the word.&lt;/li&gt;
&lt;li&gt;A is the 2nd character, E is the 5th character.&lt;/li&gt;
&lt;li&gt;S is either the 3rd or 4th character (it is not the 1st character).&lt;/li&gt;
&lt;li&gt;Only 12 possible answers are in the word list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pretty standard Wordle stuff. But let's look at how does this impacts the stats on the remaining possible answers. And how “PLUTO” was chosen.&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%2F0dhjqs93d3skoko3t15e.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%2F0dhjqs93d3skoko3t15e.png" alt="Wordle statistics"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the 2nd word characters when the answer is PAUSE.



&lt;p&gt;With only 12 words left, let’s find the most efficient way to eliminate our options.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because we already have information on the letters S, E, A, our scoring system will avoid them with a penalty (marked in red).&lt;/li&gt;
&lt;li&gt;The next biggest probabilities of characters in positions we do not know about are P, U, and T, in positions 1, 3, and 4, respectively (marked in orange).&lt;/li&gt;
&lt;li&gt;Finally, because we have positions 2 and 5 solved, we can make use of this to get additional information. So let us try our best to use the characters L &amp;amp; C.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From the following constraints, the only valid word to try was PLUTO. Although we know the letter O is not in the final answer, there is no word for “PLUTC”. Also, while the word PLUTO was not in the answer list, it was in the full word list, making it a valid guess.&lt;/p&gt;

&lt;p&gt;After the submission, the system now knows the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;L, T, O is not in the word (O is a confirmation of an assumption).&lt;/li&gt;
&lt;li&gt;P is the 1st character, U is the 3rd character.&lt;/li&gt;
&lt;li&gt;S is the 4th character (is it can no longer be the 3rd character).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This means I no longer need stats because there is only one truth: PAUSE.&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%2Frtqi4x531i4ti3b09lvn.gif" 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%2Frtqi4x531i4ti3b09lvn.gif" alt="Dancing futurama robot named bender"&gt;&lt;/a&gt;&lt;/p&gt;
bender dancing to a successful answer



&lt;h2&gt;
  
  
  But what is with the crazy guessing pattern? With MOHUR, BLYPE, and finally ULCER
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The Mohur is a gold coin that was formerly minted by several governments, including British India and some of the princely states which existed alongside it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The statistics here is being evaluated live and the changes are dependent on the result of each round. So the result differed where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Letter S, A, I, N is not in the word.&lt;/li&gt;
&lt;li&gt;Letter E may be in positions 1, 2, 3, 4 but not 5.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Filtering out the possible answer list leads to the following statistics:&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%2Frrtoofpmulah7h148mau.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%2Frrtoofpmulah7h148mau.png" alt="Statistics for 2nd word"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the 2nd word characters. When the answer is ULCER.



&lt;p&gt;This may not make sense at first because the word does not start with C or B. With 187 words left, this is where the details of the scoring start to matter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Once again, we ignore the letter E, which is the information we already know (in red).&lt;/li&gt;
&lt;li&gt;The letter R at the end has the highest weightage (of 62 words) by a large margin. Finding out the answer for this character either halves (no match) the possibility list, or one-thirds it (it matches).&lt;/li&gt;
&lt;li&gt;From here, the next step is finding the word with the most matches against other letters in the respective positions such as O at position 2 (score of 37), followed by any remaining characters. Matching either their positional list or unique letters list.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This may not be the most optimal choice given the known answer list. However, this is intentional because, we want to focus on the information again until we have a very high confidence in a word. And in this process, we would penalize duplicate letters to focus on reducing the number of options.(Arguably there is room for improvements here)&lt;/p&gt;

&lt;p&gt;The results from the guess were interesting.&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%2F7yys6267a1g40ng8xveo.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%2F7yys6267a1g40ng8xveo.png" alt="Statistics for 3rd word"&gt;&lt;/a&gt;&lt;/p&gt;
Statistics of the 3rd-word characters when the answer is ULCER.



&lt;p&gt;As weird and confusing as the word MOHUR was, the result reduced the possibility to 12 words. Once again, it tries to prioritise’s trying new characters and giving it the obscure word of BLYPE.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;BLYPE: a piece or shred of skin&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This word reduces the list of possibilities down to one word - ULCER, which is the final answer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Side Note:&lt;/strong&gt; &lt;em&gt;If you notice, it is willing to try characters it knows that is not in the official answer list. This is intentional, and helps take into account Wordle clones because if the actual answer chosen is not within the original answer list, the system will automatically fall back to using the full word list instead. Making this more resilient against Wordle varients.&lt;/em&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%2F7i11k40kme3hvshmuuao.gif" 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%2F7i11k40kme3hvshmuuao.gif" alt="Small minion shouting warning via loud hailer"&gt;&lt;/a&gt;&lt;/p&gt;
Minion issuing a warning



&lt;p&gt;&lt;strong&gt;⚠️ Code warning ahead:&lt;/strong&gt; If you were only here for the math and stats then skip to the end. The remaining content in this article consists of JS code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Show me the code
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;The full solving class can be &lt;a href="https://github.com/uilicious/wordle-solver-and-tester/blob/main/wordle-article-notes/WordleSolvingAlgo.js" rel="noopener noreferrer"&gt;found here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This article will be focusing on the core functionality that is required to make this process work and not all parts of the code. If you have not read Part 1, &lt;a href="https://uilicious.com/blog/automate-wordle-via-uilicious/" rel="noopener noreferrer"&gt;read it here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The code here has been simplified to skip the "boilerplate" stuff.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The solver needs to do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Given the current game state, filter the possible word list.&lt;/li&gt;
&lt;li&gt;Given the filtered word list, calculate the statistics.&lt;/li&gt;
&lt;li&gt;Given the statistics, and game state, score the word following the strategy.&lt;/li&gt;
&lt;li&gt;Suggest a word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let us break it down, piece by piece.&lt;/p&gt;

&lt;h2&gt;
  
  
  Normalize The game state object (generated in part 1)
&lt;/h2&gt;

&lt;p&gt;For every round, the game state would be generated with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;.history [ ] : An array of past wordle guesses.&lt;/li&gt;
&lt;li&gt;.pos [ ]: An array of objects containing the following info.

&lt;ul&gt;
&lt;li&gt;.hintSet : set of characters that are valid "hints".&lt;/li&gt;
&lt;li&gt;.foundChar: characters that are confirmed for the given position.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This is generated by using the information on the screen in part 1.&lt;/p&gt;

&lt;p&gt;However, for our use case, we would need to normalize some of the common data set we would need. Through the &lt;code&gt;_normalizeStateObj&lt;/code&gt; function, we get the following.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;badCharSet: Characters we know that is not in the word;&lt;/li&gt;
&lt;li&gt;goodCharSet: Characters we have confirmed are in the word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is easily generated by iterating &lt;code&gt;.history&lt;/code&gt;, and the &lt;code&gt;.pos&lt;/code&gt; data for building the good character list first. Then using that to build the bad character list inversely against the historical word list.&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="cm"&gt;/**
 * Given the state object, normalize various values, using the minimum "required" value.
 * This does not provide as much data as `WordleSolvingAlgo` focusing on the minimum required
 * to make the current system work
 * 
 * @param {Object} state 
 * 
 * @return {Object} state object normalized
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;_normalizeStateObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Setup the initial charset&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badCharSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets build the good charset&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;wordLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;foundChar&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;foundChar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets iterate history and build badCharSet&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&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;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// check the individual char&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;w&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// If char is not in good set&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="c1"&gt;// its in the bad set&lt;/span&gt;
                &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the normalize state object&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&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;
  
  
  Filtering the possible word list
&lt;/h2&gt;

&lt;p&gt;Now that we have the current game state, let us look into filtering the word list:&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="cm"&gt;/**
 * Given the wordList, filter only for possible answers, using the state object.
 * And returns the filtered list. This function just returns the wordList, if state == null
 * @param {Array&amp;lt;String&amp;gt;} wordList 
 * @param {Object} state 
 */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;filterWordList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip if its not setup&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;state&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Get the word length&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;wordLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Filter and return&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Filtering logic&lt;/span&gt;
        &lt;span class="c1"&gt;// ....&lt;/span&gt;

        &lt;span class="c1"&gt;// all checks pass, return true&lt;/span&gt;
        &lt;span class="k"&gt;return&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the filtering logic, we first remove the words within the badCharSet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filter out invalid words (aka hard mode)&lt;/span&gt;
&lt;span class="k"&gt;for&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;bad&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;badCharSet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// PS : this does nothing if the set is empty&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;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bad&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="kc"&gt;false&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;Followed by filtering out the words with wrong hint locations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filter out words with wrong hint locations, for each character position&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;wordLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the word character&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if the chracter, conflicts with an existing found char (green)&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;foundChar&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;sChar&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;foundChar&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="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Check if the character is already a known mismatch (yellow, partial match)&lt;/span&gt;
    &lt;span class="c1"&gt;// for each position&lt;/span&gt;
    &lt;span class="k"&gt;for&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;bad&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;hintSet&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="nx"&gt;sChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;bad&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="kc"&gt;false&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;For the subsequent words without all the known found (exact, and partial) matches:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// filter out words WITHOUT the hinted chars&lt;/span&gt;
&lt;span class="c1"&gt;// PS : this does nothing if the set is empty&lt;/span&gt;
&lt;span class="k"&gt;for&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;good&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&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="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;good&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="kc"&gt;false&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;In addition, we have a variant to filter out unique words for &lt;code&gt;filterForUniqueWordList&lt;/code&gt;. This has no character duplicates and  is used in the first few rounds:&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;let&lt;/span&gt; &lt;span class="nx"&gt;wordCharSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// iterate the characters&lt;/span&gt;
&lt;span class="k"&gt;for&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;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Update the word charset&lt;/span&gt;
    &lt;span class="nx"&gt;wordCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// There is duplicate characters&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;wordCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;
  
  
  Generating the word statistics
&lt;/h2&gt;

&lt;p&gt;After filtering for all possible answers remaining, statistics are generated via &lt;code&gt;charsetStatistics( dictArray )&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is done by building an object for the type of statistics. Iterate the word list and incrementing the numbers:&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="cm"&gt;/**
 * Analyze the given dictionary array, to get character statistics
 * This will return the required statistics model, to be used in guessing a word.
 * 
 * Which is provided in 3 major parts, using an object, which uses the character as a key, followed by its frequency as a number
 * 
 * - overall : Frequency of apperance of each character
 * - unique  : Frequency of apperance of each character per word (meaning, duplicates in 1 word is ignored)
 * - positional : An array of object, which provides the frequency of apperance unique to that word position
 * 
 * Note that because it is possible for the dataset to not have characters in the list / positional location,
 * you should assume any result without a key, means a freqency of 0
 * 
 * @param {Array&amp;lt;String&amp;gt;} dictArray - containg various words, of equal length
 * 
 * @return Object with the respective, overall / unique / positional stats
 **/&lt;/span&gt;
&lt;span class="nf"&gt;charsetStatistics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Safety check&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;dictArray&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="s2"&gt;`Unexpected empty dictionary list, unable to perform charsetStatistics / guesses`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// The overall stats, for each character&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;overallStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="c1"&gt;// The overall stats, for each unique charcter &lt;/span&gt;
    &lt;span class="c1"&gt;// (ignore duplicates in word)&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;overallUniqueStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;

    &lt;span class="c1"&gt;// The stats, for each character slot&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;positionalStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets initialize the positionalStats&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wordLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;wordLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;positionalStats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets iterate the full dictionary&lt;/span&gt;
    &lt;span class="k"&gt;for&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;word&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;dictArray&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// Character set for the word&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// For each character, populate the overall stats&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;wordLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Get the character&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Increment the overall stat&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;overallStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Populate the charset, for overall unique stats&lt;/span&gt;
            &lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Increment each positional stat&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;positionalStats&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Populate the unique stats&lt;/span&gt;
        &lt;span class="k"&gt;for&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;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;charSet&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// Increment the overall unique stat&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;overallUniqueStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets return the stats obj&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;overall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;overallStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;overallUniqueStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;positional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;positionalStats&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 is fairly straightforward for loopings across every word and every character increment within the respective statistical count.&lt;/p&gt;

&lt;p&gt;The only gotcha is that we are unable to do a ++ increment on an object property when it's not initialized. This would result in the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This will give an exception for &lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: Cannot read properties of undefined (reading 'a')&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we would need to use a simple helper function to increment our needed use case properly:&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="cm"&gt;/**
* Increment an object key, used at various stages of the counting process
* @param {Object} obj 
* @param {String} key 
**/&lt;/span&gt;
&lt;span class="nf"&gt;_incrementObjectProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&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="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="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="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&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;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fh2lkhv3vp4e62mf7vrqo.gif" 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%2Fh2lkhv3vp4e62mf7vrqo.gif" alt="Judges voting"&gt;&lt;/a&gt;&lt;/p&gt;
Judges giving their score



&lt;h2&gt;
  
  
  Scoring each word
&lt;/h2&gt;

&lt;p&gt;At the heart of the solver is the scoring logic. Which is ranked on every possible word input with the given statistics and state.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: I do not claim that this is the most optimal word scoring function there is for Wordle. It definitely can be improved, but it’s quite good from my testing so far. =)&lt;/em&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="cm"&gt;/**
* The heart of the wordle solving system.
* 
* @param {Object} charStats, output from charsetStats 
* @param {String} word to score 
* @param {Object} state object (to refine score)
* 
* @return {Number} representing the word score (may have decimal places)
**/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;scoreWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Character set for the word, used to check for uniqueness&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charSet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// the final score to return&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Wordle Strategy note:&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// - Penalize duplicate characters, as they limit the amount of information we get&lt;/span&gt;
    &lt;span class="c1"&gt;// - Priotize characters with high positional score, this helps increase the chances of "exact green matches" early&lt;/span&gt;
    &lt;span class="c1"&gt;//   reducing the effort required to deduce "partial yello matches"&lt;/span&gt;
    &lt;span class="c1"&gt;// - If there is a tie, in positional score, tie break it with "unique" score and overall score&lt;/span&gt;
    &lt;span class="c1"&gt;//   this tends to be relevent in the last &amp;lt;100 matches&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// - We used to favour positional score, over unique score in the last few rounds only&lt;/span&gt;
    &lt;span class="c1"&gt;//   but after several trial and errors run, we found it was better to just use positonal score all the way&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets do scoring math&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;

    &lt;span class="c1"&gt;// Return the score&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;score&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 goes through various stages: First, we add a safety net to prevent the system from suggesting a word again (huge negative score).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Skip attempted words - like WHY ???&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;state&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&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="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1000&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="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;We then iterate each character of the word, and score them respectively:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// For each character, populate the overall stats&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the character&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Does scoring for each character&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;Penalizing words with repeated characters or known characters&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// skip scoring of known character matches&lt;/span&gt;
&lt;span class="c1"&gt;// or the attempted character hints&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;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip known chars (good/found)&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;foundChar&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip scoring of duplicate char&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;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Skip known chars (good/found)&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goodCharSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;continue&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;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Skip scoring of duplicate char &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;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Populate the charset, we check this to favour words of unique chars&lt;/span&gt;
&lt;span class="nx"&gt;charSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;char&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we  calculate the score for each positional statistic with the unique character scoring being used as tie-breakers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Dev Note:&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// In general - we should always do a check if the "character" exists in the list.&lt;/span&gt;
&lt;span class="c1"&gt;// This helps handle some NaN situations, where the character has no score&lt;/span&gt;
&lt;span class="c1"&gt;// this is possible because the valid list will include words, that can be inputted&lt;/span&gt;
&lt;span class="c1"&gt;// but is not part of the filtered list - see `charsetStatistics`&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;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;positional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;char&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;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;positional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;10000&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="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// -- Loops to the next char -- //&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the scoring function we can start putting together all of the parts for the "suggestWord" function.&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%2Faws4cy6euy3vgrbcnkah.gif" 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%2Faws4cy6euy3vgrbcnkah.gif" alt="Simpsons lisa trying to choose a pet"&gt;&lt;/a&gt;&lt;/p&gt;
The simpsons : you've just gotta choose one



&lt;h2&gt;
  
  
  Suggesting a word (putting it together)
&lt;/h2&gt;

&lt;p&gt;We have statistics that can then be used to score words. Now, let us put it together to suggest the best scoring word.&lt;/p&gt;

&lt;p&gt;We start with being given the game state:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;normalize game state;&lt;/li&gt;
&lt;li&gt;generate filtered, unique, and full word lists
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* Given the minimum state object, suggest the next word to attempt a guess.
* 
* ---
* # "state" object definition
* 
* The solver, requires to know the existing wordle state information so this would consist of (at minimum)
* 
* .history[]    : an array of past wordle guesses
* .pos[]        : an array of objects containing the following info
*    .hintSet   : set of characters that are valid "hints"
*    .foundChar : characters that are confirmed for the given position
* 
* The above is compliant with the WordleAlgoTester state object format
* Additional values will be added to the state object, using the above given information
* --- 
* 
* @param {Object} state 
* 
* @return {String} word guess to perform
*/&lt;/span&gt;
&lt;span class="nf"&gt;suggestWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Normalize the state object&lt;/span&gt;
    &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;_normalizeStateObj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Let'sLets get the respective wordlist&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;fullWordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterWordList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filteredWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;uniqueWordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterForUniqueWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniqueWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// As an object&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;full&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;uniqueWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets do work on the various wordlist, and state&lt;/span&gt;
    &lt;span class="c1"&gt;// this is refactored as `suggestWord_fromStateAndWordList`&lt;/span&gt;
    &lt;span class="c1"&gt;// in the code base&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;Once we have the various game states and word lists, We can decide on the "statistics word list", which we use to generate the statistics model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Let's decide on which word list we use for the statistics&lt;/span&gt;
&lt;span class="c1"&gt;// which should be the filtered word list **unless** there is &lt;/span&gt;
&lt;span class="c1"&gt;// no possible answers on that list, which is possible when&lt;/span&gt;
&lt;span class="c1"&gt;// the system is being used against a WORDLE variant&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="c1"&gt;// In such a case, lets fall back to the filtered version of the "full&lt;/span&gt;
&lt;span class="c1"&gt;// word list", instead of the filtered version of the "answer list".&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;statsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&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;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[WARNING]: Unexpected empty 'filtered' wordlist, with no possible answers : falling back to full word list&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;statsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filterWordList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&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="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[WARNING]: Unexpected empty 'filtered' wordlist, with no possible answers : despite processing from full list, using it raw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;statsList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&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;Once we decide on the stats word list, we receive  the stats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Get the charset stats&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;charsetStatistics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;statsList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we decide the word list to use in deciding a word. We refer to this list as the "scoredList".&lt;/p&gt;

&lt;p&gt;In the first few rounds, we aim to use unique words as much as possible. Which would not include characters that we have tried previously. This may include words we know are not in the possible answer list.&lt;/p&gt;

&lt;p&gt;This is intentional as we are optimizing for information gain but instead over a small random chance of early success.&lt;/p&gt;

&lt;p&gt;However when this is emptied, or the game is in the final few rounds, we will fall back to the full list. During the final round, we will always guess using the filtered list when possible: (just give it our best answer).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// sort the scored list, use unique words in first few rounds&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Use valid list from round 5 onwards&lt;/span&gt;
&lt;span class="c1"&gt;// or when the unique list is drained &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;scoredList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;round&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;full&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Use filtered list in last 2 round, or when its a gurantee "win"&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;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roundLeft&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roundLeft&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&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;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;wordList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filtered&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;Once we decided on the scoring list to apply the stats, Let us score and sort it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Self reference&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Score word sorting&lt;/span&gt;
&lt;span class="nx"&gt;scoredList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;scoredList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Get the score&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;bScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scoreWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalStretch&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;aScore&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scoreWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;charStats&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;finalStretch&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// And arrange them accordingly&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;bScore&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;aScore&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="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="k"&gt;else&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;bScore&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nx"&gt;aScore&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Tie breakers - rare&lt;/span&gt;
        &lt;span class="c1"&gt;// as we already have score breakers in the algori&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;b&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&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="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="k"&gt;else&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;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&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="c1"&gt;// Equality tie ???&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And return the highest scoring item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Return the highest scoring word guess&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;scoredList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&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%2Fecswasur43zz0atud9ga.gif" 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%2Fecswasur43zz0atud9ga.gif" alt="4 pugs, dressed as the avengers"&gt;&lt;/a&gt;&lt;/p&gt;
4 pugs, dressed up as the avengers



&lt;h1&gt;
  
  
  Putting it all together
&lt;/h1&gt;

&lt;p&gt;With the UI interaction code done in part 1. Hit the "Run" button to see how our Wordle bot does!&lt;br&gt;
&lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;&lt;br&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%2F0nhdfah09z5ywrtu0tr9.gif" alt="Uilicious solving a wordle"&gt;&lt;/a&gt;&lt;/p&gt;
Screen recording of the wordle being solved



&lt;p&gt;Hey, not bad, my bot solved today's Wordle!&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%2F0rpx7d2xfxtn2shexy38.gif" 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%2F0rpx7d2xfxtn2shexy38.gif" alt="Robot friend hugging a human"&gt;&lt;/a&gt;&lt;/p&gt;
Human and robots as friends



&lt;h1&gt;
  
  
  Take away - for the humans?
&lt;/h1&gt;

&lt;p&gt;Because the bots will use a rather "inhuman" technique of calculating probabilities with giant dictionaries.&lt;/p&gt;

&lt;p&gt;Most humans will find that this is borderline of doing really weird and crazy guesses. Believe the math because it works.&lt;/p&gt;

&lt;p&gt;While playing for the human team, your takeaway from this article is that you should just start with the word "SAINE", or whatever words you would like.&lt;/p&gt;

&lt;p&gt;It is up to you since this is your game after all! =) Have fun.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Wordling! 🖖🏼🚀&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;First published &lt;a href="https://uilicious.com/blog/how-saine-mathematically-beats-crane-as-one-of-the-best-wordle-starting-word/" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>fun</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Created a Bot to Solve Wordle so I Never Have to Ever Again</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Sat, 12 Feb 2022 05:05:17 +0000</pubDate>
      <link>https://dev.to/uilicious/i-suck-at-wordle-so-i-solved-it-with-js-and-uilicious-4hfc</link>
      <guid>https://dev.to/uilicious/i-suck-at-wordle-so-i-solved-it-with-js-and-uilicious-4hfc</guid>
      <description>&lt;p&gt;TLDR: I wrote a Wordle solver bot with Javascript and UIlicious. You can run &lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;this snippet&lt;/a&gt; any day to get your daily Wordle solution. Try and see if you can get a better score than the bot! &lt;strong&gt;Feel free to edit it and optimise the solver algorithm!&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The wordler solver is covered in 3 parts, which covers the&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UI interaction code (this article)&lt;/li&gt;
&lt;li&gt;Wordle Statistical model, and the math behind it &lt;a href="https://dev.to/uilicious/saine-is-mathematically-one-of-the-best-wordle-starting-words-heres-why-2396"&gt;(link here)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Unit testing and benchmarking of the wordle solver (@ TODO) &lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;&lt;br&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%2F0nhdfah09z5ywrtu0tr9.gif" alt="Uilicious solving a wordle"&gt;&lt;/a&gt;&lt;/p&gt;
You can edit or rerun the script, by clicking on the GIF



&lt;p&gt;If you haven't already heard of Wordle, it's a fun puzzle game that challenges you to guess a valid five-letter word in six tries or less.&lt;/p&gt;

&lt;p&gt;My wife is really addicted to Wordle, and she's been bugging me to try it every day. I suppose for her, there's a thrill in getting the right word in the least amount of tries. And because we're both gamers and math-nerds, naturally we talk about strategies to solve Wordle in the least number of guesses. But when it comes to actually putting those theories to use and trying my hand at Wordle, I came to realise...&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I suck at Wordle!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;English isn't my strong suite per say...&lt;/p&gt;

&lt;p&gt;I speak Javascript better.&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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fharry-potter-allora.gif" 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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fharry-potter-allora.gif" alt="harry-potter-allora"&gt;&lt;/a&gt;I speak many languages, including Python ;-)&lt;/p&gt;

&lt;p&gt;So what better way to solve Wordle, then to write a program to solve it for me. HA!&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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fbill-nye-mind-blown.gif" 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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fbill-nye-mind-blown.gif" alt="Bill Nye shouting "&gt;&lt;/a&gt;Let's science this @*#$!&lt;/p&gt;

&lt;p&gt;After a weekend of hacking I finshed coding up my Wordle solver bot using JS and UIlicious, and I would say it works pretty well, and has managed to make the correct guess mostly within 3-4 tries so far. &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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-10-at-4.42.13-PM-2.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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-10-at-4.42.13-PM-2.png" alt="A solved Wordle using UIlicious"&gt;&lt;/a&gt;&lt;/p&gt;
Tada, solved wordle with a bot, on UIlicious



&lt;p&gt;It has mostly stirred up my wife's competitive spirit and annoyed her, since the bot does better than her on most days. But at least she's not bugging me everyday to play Wordle anymore.&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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fwomanyellingcat-1573233850.jpg" 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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2Fwomanyellingcat-1573233850.jpg" alt="Women yelling at cat"&gt;&lt;/a&gt;Is it cheating to write a bot? No, I'd say it's totally fair game!&lt;/p&gt;

&lt;p&gt;Curious to find out how the Wordle solver work? Let me explain!&lt;/p&gt;
&lt;h2&gt;
  
  
  Basic setup
&lt;/h2&gt;

&lt;p&gt;We're going to use &lt;a href="https://uilicious.com" rel="noopener noreferrer"&gt;UIlicious&lt;/a&gt; to interact with the Wordle website, fill in our guesses, and evaluate the hints. While UIlicious is primarily a cross-browser UI testing tool, nobody said that's all you can use it for, so why not solve some Wordles with it. &lt;/p&gt;

&lt;p&gt;Let's setup our solver bot on &lt;a href="https://snippet.uilicious.com" rel="noopener noreferrer"&gt;UIlicious Snippets&lt;/a&gt; - think of it as Codepen, but for UI testing, and it's entirely free to use for executing tests on Chrome. UIlicious lets you run Javascript, so I'm going to be writing the solver in JS.&lt;/p&gt;

&lt;p&gt;The first thing we'll have to do get the bot to navigate to the Wordle site. We use the &lt;code&gt;I.goTo&lt;/code&gt; command to go to the official Worldle site (&lt;a href="https://www.powerlanguage.co.uk/wordle/" rel="noopener noreferrer"&gt;https://www.powerlanguage.co.uk/wordle/&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.powerlanguage.co.uk/wordle/&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;Edit: Since the acquisition of Wordle by NY Times, there's now a cookie banner, which we need to either accept or dismiss, using the &lt;code&gt;I.click&lt;/code&gt; command.&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;I&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// to reject cookies and close the cookie banner&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's just make sure the app has loaded, with quick check for the phrase "Guess the Wordle" using the &lt;code&gt;I.see&lt;/code&gt; command:&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;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Guess the Wordle&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;Finally, let's dismiss the tutorial popup by using &lt;code&gt;I.click&lt;/code&gt; with a pixel offset to click outside the popup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// clicks 40px down and 80px right from the top-left corner of the webpage&lt;/span&gt;
&lt;span class="nx"&gt;I&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/html/body&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, we'll just hit that "Run" button to run the browser automation script, and make sure this brings us to an empty Wordle board to play with.&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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.26.30-AM.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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.26.30-AM.png" alt="Navigating to Wordle with UIlicious"&gt;&lt;/a&gt;Now we're ready to Wordle!&lt;/p&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;Ok, now let's dive into writing more complex parts of the solver.&lt;/p&gt;

&lt;p&gt;The bot will need to be able to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enter letters into Wordle&lt;/li&gt;
&lt;li&gt;Read the hints from Wordle&lt;/li&gt;
&lt;li&gt;Find the solution based on the hints (I'll explain this in a follow up post)&lt;/li&gt;
&lt;/ol&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%2Fc.tenor.com%2FuYqsM9uIyuYAAAAC%2Fsimple-easy.gif" 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%2Fc.tenor.com%2FuYqsM9uIyuYAAAAC%2Fsimple-easy.gif" alt="Man snapping fingers, saying "&gt;&lt;/a&gt;This will be a piece of cake!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Entering letters into Wordle
&lt;/h2&gt;

&lt;p&gt;We've only got up to six tries to guess the Wordle, so we'll start with a simple &lt;code&gt;for&lt;/code&gt; loop to make 6 attempts to guess the word:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// solver code will go in here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we're going to generate a couple of words and enter our guesses into the game. I'm not going to dive into how &lt;code&gt;solver.suggestWord(gameState)&lt;/code&gt; works at the moment, but what it basically does is suggest a word based on the previous guesses and the revealed hints. I'll explain how the solver works in a follow-up post, stay tuned!&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;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
  &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After computing the &lt;code&gt;guessWord&lt;/code&gt;, I need the bot to enter the word into the game board, which is pretty straightforward. I use the command &lt;code&gt;I.type&lt;/code&gt; to enter the guess word, and &lt;code&gt;I.pressEnter&lt;/code&gt; to submit the guess. I've also added a small wait for the game board to finish its animation and allow input for the next guess, because the solver can be too fast sometimes and might attempt its next guess before the game board is ready.&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;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&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="c1"&gt;// Does some stuff to get gameState&lt;/span&gt;

  &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
  &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// enter and submit the guess word into the Wordle game board&lt;/span&gt;
  &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// add a small wait for the animation to finish before entering the next guess&lt;/span&gt;
  &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Reading the hints from Wordle
&lt;/h2&gt;

&lt;p&gt;The next important step is after each guess, we need to evaluate the hints to make our next guess. This is a bit trickier, as we'll need to do a bit of scraping to get our hints from the HTML. &lt;/p&gt;

&lt;p&gt;First, lets grab each row of the game board. The Wordle app is entirely written using Web Components (impressive!), so we'll need to make use of &lt;code&gt;document.querySelector&lt;/code&gt; and &lt;code&gt;.shadowRoot&lt;/code&gt; to dive into the &lt;code&gt;&amp;lt;game-app&amp;gt;&lt;/code&gt; element and grab each of the &lt;code&gt;&amp;lt;game-row&amp;gt;&lt;/code&gt; element which stores important information about the revealed hints.&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;let&lt;/span&gt; &lt;span class="nx"&gt;rowList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-theme-manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
        &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#board&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-row&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;Here's what we'll get when we run this query in the browser console:&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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.39.42-AM.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%2Fuilicious.com%2Fblog%2Fcontent%2Fimages%2F2022%2F02%2FScreenshot-2022-02-12-at-1.39.42-AM.png" alt="Inspecting the Wordle game-row element"&gt;&lt;/a&gt;Oh look what we have here!&lt;/p&gt;

&lt;p&gt;Each of these &lt;code&gt;game-row&lt;/code&gt; element has two very useful data attributes that we want: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;_letters&lt;/code&gt;: tells you guess that you've made for this row, e.g. &lt;code&gt;"hello"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;_evaluation&lt;/code&gt;: an array of hints for each letter, e.g. &lt;code&gt;["absent", "present", "correct", "absent", "absent"]&lt;/code&gt;. &lt;code&gt;"correct"&lt;/code&gt; if the letter is in the correct position. &lt;code&gt;"present"&lt;/code&gt; if the letter is in the word, but is in the wrong position. And &lt;code&gt;"absent"&lt;/code&gt; if the letter isn't in the word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we're going to build a &lt;code&gt;state&lt;/code&gt; object to store all of these information and pass it to our solver. The code below is nothing too fancy. We'll wrap this in a function because we want to run it after each guess to get the updated hints.&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="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// We prepare the state object which we would want to return&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;history&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="na"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;},{&lt;/span&gt; &lt;span class="na"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;:[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets get the list of result rows from the UI DOM&lt;/span&gt;
    &lt;span class="c1"&gt;// unfortunately this is more complicated then needed due to shadowDoms&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;rowList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;shadowRoot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-theme-manager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#board&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;game-row&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;//&lt;/span&gt;

    &lt;span class="c1"&gt;// Lets build the state by reading each row&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;rowList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rowList&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="c1"&gt;// a row can be blank, if there was no guess made&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;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_letters&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_letters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_letters&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="c1"&gt;// Add it to history list&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;history&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="c1"&gt;// Iterate each char in the word&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_evaluation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;absent&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="c1"&gt;// does nothing&lt;/span&gt;
                    &lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_evaluation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;present&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;hintSet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;c&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_evaluation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;correct&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;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;foundChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;c&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;throw&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unexpected row evaluation : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_evaluation&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="c1"&gt;// Configure the final "allCorrect" state&lt;/span&gt;
            &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;allCorrect&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// And return the state obj&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;state&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 piece of code above needs to be executed the browser, so I need to tell the Wordle bot (which runs on a sandboxed NodeJS instance) to run the funciton in the browser using &lt;code&gt;UI.execute&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;UI&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
    &lt;span class="c1"&gt;// put the above code snippet in here to get the game state&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;Okay, now we want to use this function to get the game state after each guess, so let's go back to our &lt;code&gt;for&lt;/code&gt; loop and update it:&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;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// get the game state&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// if we got all the letters correct, we've won, hurray!&lt;/span&gt;
    &lt;span class="c1"&gt;// exit the loop in that case&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;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
    &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// enter and submit the guess word into the Wordle game board&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// add a small wait for the animation to finish before entering the next guess&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whew! Now that the bot can enter its guesses into the board and get the revealed hints, we can work on writing the algorithm to solve the Wordle in six attempts or less!&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Finding the solution based on the hints... in a follow up post!
&lt;/h2&gt;

&lt;p&gt;Writing the solver algoritm is the biggest challenge, and a whole lot of fun. I spent quite a bit of time reading up on optimal strategies (there's even papers written about this), and tweaking the algoritm. But this deserves a post on it's own, so I'm going to write a follow up post after, stay tune!&lt;/p&gt;

&lt;h3&gt;
  
  
  Putting everything together
&lt;/h3&gt;

&lt;p&gt;Finally, let's put together the wordle solver:&lt;/p&gt;

&lt;p&gt;Before the start of the &lt;code&gt;for&lt;/code&gt; loop, we'll initialise the solver with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WordleSolvingAlgo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And after we exit the &lt;code&gt;for&lt;/code&gt; the loop, we want to query the game state again. At this point, we've either guessed the correct word in less than 6 attempts, or have made 6 guesses but don't know what's the final outcome. We'll get the final game state one more time, and report the outcome using &lt;code&gt;TEST.log.pass&lt;/code&gt; if the bot won, or &lt;code&gt;TEST.log.fail&lt;/code&gt; if the bot lost game.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// this comes after exiting the for loop...&lt;/span&gt;

&lt;span class="c1"&gt;// get the final game state&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// is it all correct? &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;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// CONGRATS!!!&lt;/span&gt;
    &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NEXT WORDLE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The wordle is : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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="c1"&gt;// DARN! Try better next time!&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The last guess was : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;Here's what everything looks altogther (except for the solver algo part!)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Open Wordle!&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://www.powerlanguage.co.uk/wordle/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// to reject cookies and close the cookie banner&lt;/span&gt;
&lt;span class="nx"&gt;I&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Reject&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

&lt;span class="c1"&gt;// Is Wordle loaded?&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Guess the Wordle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Solve Wordle!&lt;/span&gt;
&lt;span class="nf"&gt;runTheWordleSolver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;//----------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;// This function runs the wordle solver,&lt;/span&gt;
&lt;span class="c1"&gt;// which will make up to 6 attempts to guess the word&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runTheWordleSolver&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// initialise the solver&lt;/span&gt;
    &lt;span class="c1"&gt;// stay tune for more about the solving algorithm!&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WordleSolvingAlgo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fullWordList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;filteredWordList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// make up to 6 guesses&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// get the game state (includes the revealed hints)&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// if we got all the letters correct, we've won, hurray!&lt;/span&gt;
        &lt;span class="c1"&gt;// exit the loop in that case&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;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// guess the next word&lt;/span&gt;
        &lt;span class="nx"&gt;guessWord&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;solver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;suggestWord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;gameState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// enter and submit the guess word into the Wordle game board&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// add a small wait for the animation to finish before entering the next guess&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;wait&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="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// at this point, we either guessed the correct word in less than 6 attempts, or have made 6 guesses and don't know what's the outcome yet.&lt;/span&gt;

    &lt;span class="c1"&gt;// get the final game state&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gameState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getWordleStateFromUI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// is it all correct? &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;gameState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;allCorrect&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// CONGRATS!!!&lt;/span&gt;
        &lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;NEXT WORDLE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The wordle is : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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="c1"&gt;// DARN! Try better next time!&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;==== END OF GAME ====&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nx"&gt;TEST&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;## The last guess was : &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;guessWord&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&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;h2&gt;
  
  
  Drumroll... The moment of Truth!
&lt;/h2&gt;

&lt;p&gt;Let's smash the "Run" button and see how our Wordle bot does!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/embed/test/public/N5qZKraAaBsAgFuSN8wxCL?stepNum=22&amp;amp;autoplay=0" rel="noopener noreferrer"&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%2Ffkk0t42h4ns0nmwpyxz8.png" alt="Solved Wordle"&gt;&lt;/a&gt;&lt;/p&gt;
Hey, not bad, my bot solved today's Wordle in 4 attempts!



&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%2Fc.tenor.com%2FvMCk9U93WkcAAAAC%2Fjim-carrey-aceventura.gif" 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%2Fc.tenor.com%2FvMCk9U93WkcAAAAC%2Fjim-carrey-aceventura.gif" alt="Jim Carrey says "&gt;&lt;/a&gt;Mmmmm, sweet sweet glory!&lt;/p&gt;

&lt;p&gt;Stay tuned for part 2. I'll explain the solver engine works, and the math behind it. Or if you want to dive into the full code yourself, you can check it out &lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;over here&lt;/a&gt;, and maybe even replace &lt;code&gt;WordleSolvingAlgo&lt;/code&gt; with your own algorithm to solve Wordle: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL" rel="noopener noreferrer"&gt;https://snippet.uilicious.com/test/public/N5qZKraAaBsAgFuSN8wxCL&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;At the mean time ….&lt;br&gt;
&lt;strong&gt;Happy Wordling! 🖖🏼🚀&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>fun</category>
      <category>beginners</category>
    </item>
    <item>
      <title>What is Automation Testing? Why You Should Use Automated Tests?</title>
      <dc:creator>Abhiraj Bhowmick</dc:creator>
      <pubDate>Mon, 06 Dec 2021 12:06:07 +0000</pubDate>
      <link>https://dev.to/uilicious/what-is-automation-testing-why-you-should-use-automated-tests-4edp</link>
      <guid>https://dev.to/uilicious/what-is-automation-testing-why-you-should-use-automated-tests-4edp</guid>
      <description>&lt;p&gt;We've long believed that automation testing is a wonderful formula for improving the quality of apps right from the start. However, it is only when we begin automating that we grasp the true reality. We frequently confront issues such as deciding when to start automating tests, what tests to automate, how to choose the correct technology, and how to write automation test scripts that follow the best practices.&lt;/p&gt;

&lt;p&gt;So let’s discuss all this in detail in this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Test Automation and how does it work?
&lt;/h2&gt;

&lt;p&gt;It is a method in which automation tools run a test suite, perform predetermined actions on a software application, report on the results, compare them, and generate detailed test reports.&lt;/p&gt;

&lt;p&gt;Test automation requires financial and human resources, long development cycles, and repeated executions. However, before we begin automating tests, we must first determine the best time for automation, the breadth of automation, and the best tool for automation. It might lead your project to unexpected additional costs if this process is not done properly from the beginning.&lt;/p&gt;

&lt;p&gt;Automated tests can be done across several servers during off-peak hours (midnight) and consume a fraction of the time that manual testing takes. This implies that the developer and tester’s time is used in the most efficient way possible, and the team receives faster feedback on code quality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criteria for Automation
&lt;/h2&gt;

&lt;p&gt;To be automated, a test must match certain conditions; otherwise, it may wind up costing more than it saves. After all, saving time, effort, and money is a fundamental purpose of automation.&lt;/p&gt;

&lt;p&gt;Here are some general test automation criteria. Keep in mind that these are only suggestions. Depending on your circumstances, your criteria may differ.&lt;/p&gt;

&lt;p&gt;Repeatable: The test must be able to be repeated. Automating a test that can only be run once makes no sense. The following three steps comprise a repeatable test:&lt;/p&gt;

&lt;p&gt;Configure the test, including the data and the environment.&lt;br&gt;
Execute the function and determine the outcome.&lt;br&gt;
Clean up the data as well as the surroundings..&lt;br&gt;
We want to be able to make the environment consistent in the initial phase.&lt;/p&gt;

&lt;p&gt;Determinant: When a function is determinant, the result is the same each time it is performed with the same input. The same may be said for automated tests. Let's imagine we wish to put an additional function to the test. We now know that 1+1=2, and that 394.19 + 5.81 = 400.00. The function of addition is determinant. Software, on the other hand, may have such a large number of varied inputs that getting the same result over time is challenging. Some variables may be completely random, making it difficult to predict a specific outcome.&lt;/p&gt;

&lt;p&gt;Unopinionated: You cannot automate matters of opinion. This is where usability testing, beta testing, and so forth really shine. User feedback is important, but it just can’t be automated … sorry!&lt;/p&gt;

&lt;p&gt;Most devs say software test automation demands considerable investments of money and resources. But UI-licious makes it easy to implement and maintain. We created this all-in-one automation tool with the desire to deliver better web experiences for everyone in mind.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing at different levels
&lt;/h3&gt;

&lt;p&gt;A strategy to decide the number of tests to automate is the test automation pyramid. This strategy suggests writing three types of tests with different levels of granularity. The higher the level, the fewer tests there are to write.&lt;/p&gt;

&lt;p&gt;Unit testing offers software products with robustness as a firm base. It's simple to write and execute tests when you test particular pieces of the code.&lt;br&gt;
The service layer refers to evaluating an application's services separately from its user interface; these services include everything that the application does in response to a single or multiple inputs.&lt;br&gt;
We have UI testing at the top level, which has fewer tests due to many attributes that make them more difficult to run, such as the fragility of the tests, where a tiny change in the user interface can break a large number of tests, requiring additional maintenance effort.&lt;br&gt;
Type of automated tests&lt;br&gt;
Knowing what are the different forms of automated testing is critical when it comes to integrating test automation in your QA department. This will give you a good idea of how comprehensive a test automation program is, and if you can integrate it into your present quality assurance procedures or not. Furthermore, understanding the various sorts of tests allows you to make informed decisions about which types of testing would produce the best results for your organization.&lt;/p&gt;

&lt;p&gt;There are many different types of test automation. The following is a comprehensive list of the most common ones:&lt;/p&gt;

&lt;p&gt;Code Analysis: Static and dynamic code analysis tools are just two of the numerous types of code analysis tools available. Some of these checks are for security issues, while others are for style and form. When a developer checks in code, these tests are run. There isn't much test writing to perform with these automated tests other than configuring rules and keeping the tools up to date.&lt;br&gt;
Unit Testing: Unit testing is doing tests on individual components in isolation to ensure that they function properly. It is commonly the first type of automated testing performed on an application because it is usually done during the development phase.&lt;br&gt;
Integration Testing: Integration testing entails testing the application's various elements as a whole. When it comes to automation, integration tests are a unique kind. Integration tests, often known as end-to-end tests, are more difficult to put up since they must interface with external dependencies. When working with resources that aren't under your control, it's often beneficial to construct false external resources.&lt;br&gt;
Performance Testing: Performance testing is putting a piece of software through its paces, stability, and responsiveness while under load. The fundamental goal of performance testing is to detect and eliminate any potential performance bottlenecks so that the program can offer the best results to the end-user. Performance testing is an important part of assuring a product's market success since it helps uncover potential issues that consumers may encounter, such as slow software functioning under a heavy workload.&lt;br&gt;
Regression Testing: Regression testing is a sort of maintenance testing. It entails running functional and non-functional tests again to see if the software behaves the same way it did previously after a code or program change. The software is said to have regressed if the performance is no longer the same. The primary goal of regression testing is to guarantee that existing functionality is not harmed as a result of code changes.&lt;br&gt;
Smoke Testing: Smoke testing, also known as "Build Verification Testing" and "Confidence Testing," is a series of tests designed to evaluate the stability and viability of the software's delivered build. Smoke testing is used to determine whether an application should be shelved due to lack of functioning or moved on to the next stage of testing. Once the generated software functions have been merged with the software build, smoke testing is performed. Any failures in testing at this stage will almost always result in the application being sent back to the development team for changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use automated tests?
&lt;/h2&gt;

&lt;p&gt;Pocket-friendly&lt;br&gt;
Contrary to popular belief, automated testing can be less expensive than manual testing. You can't conduct repeating tests with manual testing. In reality, the cost of manually testing your application increases over time. Automated testing, on the other hand, is less expensive in the long run since once you've built your test scripts, you can reuse them at any time without incurring additional costs. True, automation adoption is initially costly, but it will quickly pay for itself.&lt;/p&gt;

&lt;p&gt;It's important to remember that the amount of the automated testing ROI is determined by the extent of automation adoption. The bigger the return on investment is, the more automated test cases you create and use.&lt;/p&gt;

&lt;p&gt;Time-Saving&lt;br&gt;
You can save time by automating your tests. Automated tests are quick to complete and can be repeated. To put it another way, you won't have to wait weeks to do the tests again — only a few hours will be enough.&lt;/p&gt;

&lt;p&gt;Automated testing accelerates development by taking advantage of their quick execution and repeatability. Transitioning from manual testing to automation will shorten your development time and increase your productivity.&lt;/p&gt;

&lt;p&gt;Better Accuracy&lt;br&gt;
You're more likely to have error-free releases if you use test automation. Automated testing is more accurate than manual testing because it requires less human intervention. The problem is that a human tester can make mistakes at any stage of the review process. The machine, on the other hand, will not cooperate. Because generated test cases are more precise than human testers, you can lower the likelihood of failure by removing human errors.&lt;/p&gt;

&lt;p&gt;Immediate Feedback&lt;br&gt;
Another advantage of automated testing is that it provides immediate feedback. Developers receive testing reports instantaneously with fast test execution, so they can react swiftly if a problem happens. Forget about deciphering the code that was written three weeks ago.&lt;/p&gt;

&lt;p&gt;When your app is already on the market, immediate feedback is very useful. Manual testing can simply slow down the process if you need to fix some errors immediately. Test automation, on the other hand, will allow you to make quick changes to your application. As a result, automated testing leads to increased team responsiveness, improved user experience, and increased customer satisfaction.&lt;/p&gt;

&lt;p&gt;DevOps Implementation&lt;br&gt;
Every developer's commit to the source code must be tested as part of the CI/CD pipeline, and there's no other way to accomplish it fast and efficiently than with test automation. As a result, once you've deployed automated testing, transitioning to Continuous Testing and Delivery will be simple. When the complexity of the code and the number of test cases increases, it becomes increasingly difficult to manage.&lt;/p&gt;

&lt;p&gt;The good news is that we created a solution called UI-licious which is an all-in-one automation tool that helps anyone automate tests in minutes, not in hours. With our flexible testing solution, experienced testers, as well as non-coding beginners, can ensure that crucial error scenarios are quickly identified, so users do not encounter any unexpected failures when browsing your website or web app.&lt;/p&gt;

&lt;h3&gt;
  
  
  When to use automated testing?
&lt;/h3&gt;

&lt;p&gt;If you're running the same test again and again without altering it, it's likely that automating it would save you a lot of time. That's because having a manual task done on a frequent basis wastes your team's time and is likely to result in additional errors due to inattention.&lt;/p&gt;

&lt;p&gt;Human error is no longer a possibility thanks to automation. As a result, the adoption of automated testing can drastically increase quality in particular cases.&lt;/p&gt;

&lt;p&gt;The bottom line is that if you can save money while still delivering a high-quality product, do so. This is where automation really shines. Most automation technologies aren't cheap, so the project must be large enough to justify the investment. However, UI-licious has plans starting from $12 a month, which happen to be very cost-effective and pocket-friendly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Common Misconceptions about Automation testing
&lt;/h3&gt;

&lt;p&gt;It is both true and incorrect that test automation will allow you to have more free time. The majority of manual testing time is spent on exploratory and functional testing, which involves manually searching for faults. Once that process is finished, the manual tester must perform the identical actions over and over again. That time is greatly reduced with automated testing. Instead of writing tests, automation testers spend their time coding them and improving them as needed. However, after the test is completed, automated testing enables for the reuse of tests, eliminating the need to repeat the entire procedure.&lt;/p&gt;

&lt;p&gt;Automated testing allows you to concentrate on more important topics like client needs, functionality, and enhancements. Furthermore, the software tests can be run each time the source code is amended. Manually performing these tests is time-consuming and expensive, whereas automated tests can be done repeatedly at no additional cost.&lt;/p&gt;

&lt;p&gt;Investing in automated testing may appear to be prohibitively expensive at first, especially if you're a small business. But that's not the case with UI-licious, our pricing is cost-effective because you can create tests fast. Indeed, you don't need to hire an expert who is proficient in a particular coding language to write your tests. Moreover, the monthly fee is derisory compared to most of the similar solutions.&lt;/p&gt;

&lt;p&gt;Another prevalent misunderstanding concerning automated testing is that it eliminates the need for human involvement. In all honesty, automated testing is more precise and faster than what people can accomplish without incurring significant human error, thus this misunderstanding is acceptable. This does not replace the need for face-to-face communication in software development. Instead, it increases that aspect by giving an additional communication route.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to and not to automate
&lt;/h3&gt;

&lt;p&gt;Without necessarily automating tests end-to-end, testing tools can assist with tasks such as product installation, test data creation, GUI interaction, problem detection (think parsing or polling agents equipped with test oracles), defect tracking, and so on.&lt;/p&gt;

&lt;p&gt;We shouldn't try to automate negative or failover tests since they need testers to think analytically, and negative tests aren't always easy to offer a pass or fail result that can help us.&lt;/p&gt;

&lt;p&gt;Who should be involved with test automation?&lt;br&gt;
With short Agile iterations, testing frequently needs a "shift left" strategy. Because of the shift to the left in the agile development process, testing can begin considerably earlier in the application lifetime. As a result of this strategy, developers with strong technical skills are increasingly being held responsible for testing, and they frequently collaborate with testers to design test automation frameworks. When assessing a testing solution, it's critical to find one that meets the requirements of all team members who will be involved in the testing process. Manual testers, automation engineers, and developers are among them.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to automate your tests?
&lt;/h3&gt;

&lt;p&gt;If you are wondering how to start with automation testing, you can follow this easy 5 step process.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Select software or tools for testing.&lt;br&gt;
After you've decided which test to automate, you may look at the various testing tools accessible. Consider how a tool will operate with your present software platform before selecting one, as certain tools are better suited to a specific platform. Like UI-licious is a great choice because of its framework-agnostic character and cheaper solutions than most other products out there. Not only is it easy to start, but you also don’t need to master a coding language to implement your automated testing strategy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Determine which tests should be automated.&lt;br&gt;
Choose which test you wish to automate first. Manual and automated testing are used by many software engineers. Certain functionality may need to be manually tested. With UI-licious, you can get started with 0 coding knowledge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Plans, Designs, and Scripts&lt;br&gt;
After you have chosen your tools for automation, plan the design of the framework and its features. Then you can plan automation things that are in-scope and out-of-scope. Next, you can start scripting and execution of schedules and timelines&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Execute your automated tests&lt;br&gt;
During this phase, automation scripts are run. Before they may be set to run, some scripts require input test data. Once they’ve been run, they generate extensive test reports you can easily analyze and share with your team.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Maintain your test scripts&lt;br&gt;
The Test Automation Maintenance Approach is a phase of automation testing that is used to see if the new features added to the software are working properly. When new automation scripts are added, they must be reviewed and maintained in order to improve the effectiveness of automation scripts with each release cycle. UI-licious makes the maintenance easier because the scripting language relies less on CSS and XPATH selectors and more on user behaviors. Indeed, with our testing solution, you are actually creating scripts that are following the user journey you want to verify.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Conclusion&lt;br&gt;
Test automation allows testers to devote more time to exploratory tests, which often uncover more flaws than any automated test. Because of these and other factors, test automation can make you save time and money on all your projects.&lt;/p&gt;

&lt;p&gt;And so, UI-licious is there to help you out in this world of automation testing.&lt;/p&gt;

&lt;p&gt;Apart from being a flexible and accessible way to start test automation, UI-licious provides precise results after testing, and the reports are also very informative and helpful, especially when looking up the history of past tests. It also provides a monitoring tab where we can see how our automation is progressing. It has a very user-friendly interface, and even non-technical individuals can understand the process.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>beginners</category>
      <category>javascript</category>
      <category>devops</category>
    </item>
    <item>
      <title>live migrating VM's - cool tech for the future, that you should avoid using today (Review &amp; Benchmark of GCP E2 Instances)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Mon, 16 Dec 2019 15:44:43 +0000</pubDate>
      <link>https://dev.to/uilicious/live-migrating-vm-s-cool-tech-for-the-future-that-you-should-avoid-using-today-review-benchmark-of-gcp-e2-instances-5247</link>
      <guid>https://dev.to/uilicious/live-migrating-vm-s-cool-tech-for-the-future-that-you-should-avoid-using-today-review-benchmark-of-gcp-e2-instances-5247</guid>
      <description>&lt;p&gt;&lt;strong&gt;Should I buy summary: NO&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Unless you are using GCP E2 specifically for, non-CPU dependent burst workloads (eg. background tasks), temporary autoscale, or are planning to use committed use discount - stay with N1/N2.&lt;/p&gt;

&lt;p&gt;For nearly all other workloads, it makes no economic sense to use E2, due to its approximately 50% reduction in CPU performance (+/-).&lt;/p&gt;

&lt;p&gt;If you want the raw numbers, skip the prelude section and go straight to the benchmark section below. &lt;a href="https://github.com/uilicious/public-datalab/tree/master/benchmark/google-cloud/e2-instance"&gt;or alternatively our github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However after benchmarking it, and reviewing the tech behind it. I realized there is a lot more then meets the eye - how E2 could however potentially be game-changing for cloud providers in the future ... Assuming they have the "courage" to use it ...&lt;/p&gt;

&lt;p&gt;(read on to learn more)&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--wF0vuXPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hr31b1zno4eu6nqn6yz6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--wF0vuXPL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hr31b1zno4eu6nqn6yz6.png" alt="Sample Uilicious server usage"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prelude: Understanding the VM workload
&lt;/h1&gt;

&lt;p&gt;Before understanding E2, you will need to understand a typical VM workload.&lt;/p&gt;

&lt;p&gt;Where most server VM workloads, are underutilized "most of the time", with the occasional bump in resource consumption, during certain hours of the day, or when they appear on Reddit.&lt;/p&gt;

&lt;p&gt;This can be easily be observed by looking at most VM resource usage charts.&lt;/p&gt;

&lt;p&gt;The chart above is a browser test automation server, attached to one of several clients within &lt;a href="https://ulicious.com"&gt;ulicious.com&lt;/a&gt;. Notice how this server spikes twice with a small gap in between. &lt;/p&gt;

&lt;p&gt;This is extremely typical for work applications representing the start of the workday, followed by lunch, and the rest of the workday (after offsetting time of day, for different timezone)&lt;/p&gt;

&lt;p&gt;Also note, on how the system is idle under 40% most of the time (running some background tasks). &lt;/p&gt;

&lt;p&gt;What this means is that, for most data centers, thousands of servers are paid for in full, but are underutilized, and burning through &lt;a href="https://www.datacenterknowledge.com/archives/2016/06/27/heres-how-much-energy-all-us-data-centers-consume"&gt;GigaWatts of electricity&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PRimn8sX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fjogx5yglfozhv07gzn3.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PRimn8sX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/fjogx5yglfozhv07gzn3.jpeg" alt="Sunrise in the clouds"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prelude: The auto-scaling cloud era
&lt;/h1&gt;

&lt;p&gt;For infrastructure providers to lower overall cost, and pass these savings to their customers, as a competitive advantage. New ideas came along on how to safely maximize the usage of idle resources.&lt;/p&gt;

&lt;p&gt;First came auto-scaling, where VM's can be added only when needed, allowing cloud consumers to allocate only the bare minimum server resources required to handle the idle workload, and buffer surges to request as auto-scaling kicks in (there is a time lag). &lt;/p&gt;

&lt;p&gt;Significantly cutting down on idle resource wastage.&lt;/p&gt;

&lt;p&gt;This change, along with changes in the billing model. Started the "cloud revolution".&lt;/p&gt;

&lt;p&gt;And it was only the beginning...&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nmf88ALf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ultuikezn3d8g8j5dxbw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nmf88ALf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ultuikezn3d8g8j5dxbw.gif" alt="Pikachu Dancing - every day I'm shufflin"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Prelude: Shuffling short-lived servers
&lt;/h1&gt;

&lt;p&gt;New ideas were then subsequently introduced for a new type of server workload, which can be easily shuffled around, to maximize CPU utilization and performance for all users.&lt;/p&gt;

&lt;p&gt;The essence of it is simple, slowly put as many applications as possible into a physical server, until it approaches near its limits (maybe. 80%), once it crosses the limit, pop out an application and redistribute to another server - either by immediately terminating it, or waiting for its execution to be completed and redirecting its workload.&lt;/p&gt;

&lt;p&gt;This is achieved either through extremely short-lived tasks (eg. Function as a service, serverless). Or VMs which are designed to be terminated when needed (eg. Pre-emptible, spot instances).&lt;/p&gt;

&lt;p&gt;The downside of the two approaches given above, however, is that the application software may need to be re-designed to support such workload patterns.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W0LDfCLh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dem4qq2mw6joqemi9i8x.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W0LDfCLh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/dem4qq2mw6joqemi9i8x.jpeg" alt="Broken Glass, metaphor to broken rules"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  How E2 - changes the rules of cloud shuffling
&lt;/h1&gt;

&lt;p&gt;To quote the E2 announcement page ...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;After VMs are placed on a host, we continuously monitor VM performance and wait times so that if the resource demands of the VMs increase, we can use live migration to transparently shift E2 load to other hosts in the data center.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without getting too deep into how much &lt;a href="https://cloud.google.com/blog/products/compute/understanding-dynamic-resource-management-in-e2-vms"&gt;&lt;del&gt;pretty crazy and awesome&lt;/del&gt; engineering goes into writing a new hypervisor&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In a nutshell, what E2 does, is enable google to abstract a live running VM from its CPU hardware. Performing migration in between instances with near-zero ms downtime (assumption). &lt;/p&gt;

&lt;p&gt;Your VM servers can be running on physical server A in the first hour, and be running in physical server B in the next hour as its resources are being used up.&lt;/p&gt;

&lt;p&gt;So instead of shuffling around a specially designed VM workload. It can shuffle around any generic VM workload instead. Allowing much better utilization of resources, with lower cost, without reducing the user experience ... Or at least it should in theory ... &lt;/p&gt;

&lt;p&gt;Onto the benchmarks!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note, while live migration's of VM's are not new tech. This marks the first time it is integrated as part of a cloud offering, to lower the cost of the product. Also, for those who have used it before, the list of issues is endless - which google presumingly has resolved for their custom E2 instances.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Benchmarking: Show me the numbers!
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Full details on the benchmark raw numbers the steps involved can be found at &lt;a href="https://github.com/uilicious/public-datalab/tree/master/benchmark/google-cloud/e2-instance"&gt;GitHub link&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The following benchmarks were performed in &lt;code&gt;us-central1-f&lt;/code&gt; region. Using &lt;code&gt;N1/N2/E2-standard-4&lt;/code&gt; instances. With &lt;code&gt;N1-standard-4&lt;/code&gt; serving the baseline for comparison.&lt;/p&gt;

&lt;p&gt;Covering the following&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sysbench CPU&lt;/li&gt;
&lt;li&gt;Sysbench Memory&lt;/li&gt;
&lt;li&gt;Sysbench Mutex&lt;/li&gt;
&lt;li&gt;Nginx + Wrk&lt;/li&gt;
&lt;li&gt;Redis-Benchmark&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sysbench CPU
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EGA3OkHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nv7bl87k9yu6xmjcmhnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EGA3OkHI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/nv7bl87k9yu6xmjcmhnp.png" alt="CPU benchmarking summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While its no surprise that a new hypervisor designed to share CPU's across multiple workloads would be "slower". A 69% reduction might be too much for most people to stomach.&lt;/p&gt;

&lt;p&gt;However, that is not the only detail to keep track of&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gw7sliJ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hrrfws4e1nwg6sm7pj7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gw7sliJ4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/hrrfws4e1nwg6sm7pj7u.png" alt="E2 cpu run result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;E2 also sees the largest variance in request statistics, across &lt;code&gt;min/avg/95 percentile&lt;/code&gt;. This is in contrast to NX series benchmark (below) which these 3 numbers would be mostly the same.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iRtEebc_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rvzeh1pjgysmntyaf5d5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iRtEebc_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/rvzeh1pjgysmntyaf5d5.png" alt="N1 cpu run result"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Sysbench Memory / Mutex
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--IzAjrfjl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/81ldb4pgnvp6gsb2tsvw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IzAjrfjl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/81ldb4pgnvp6gsb2tsvw.png" alt="Memory benchmark summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;On the plus side, it seems like E2 instances, with the newer generation of memory hardware and clock speeds, blow pretty much N1/N2 instance workload out of the water. By very surprisingly large margins.&lt;/p&gt;

&lt;p&gt;So it's +1/-1 for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workload benchmark: Nginx + Wrk, Redis-Benchmark
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5vL3yBM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xk0tdba1nacipm05fxhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5vL3yBM_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xk0tdba1nacipm05fxhp.png" alt="Workload Benchmark Summary"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, despite much better memory performance. The penalty in CPU performance results in an approximate &lt;code&gt;~50%&lt;/code&gt; reduction in workload performance for even memory-based workloads.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lies, damned lies and benchmarks&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;These numbers are just indicators for comparison between equivalent GCP instance types. As your application workload may be a unique snowflake, there will probably be differences which you may want to benchmark on your own.&lt;/p&gt;

&lt;p&gt;Also this is meant for GCP to GCP comparison, and not GCP to other cloud provider comparison.&lt;/p&gt;

&lt;p&gt;Note, that as I am unable to induce a live migration event, and benchmark its performance under such load. Until we can find a way to get data on this, let's just presume its in milliseconds? maybe? (not that it changes my review)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Pricing Review: is it worth it ??
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--J8rJjAke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q1s66irv6xbsyhb97fhr.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--J8rJjAke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q1s66irv6xbsyhb97fhr.jpeg" alt="sorry but no"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In case that was not clear: NO&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If it was a poorer performer at a lower price E2 would make for a compelling offer. This, however, is what's the confusing thing about the E2 launch. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hg6q7RCy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/shabjfrldzjwayrmdrfl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hg6q7RCy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/shabjfrldzjwayrmdrfl.png" alt="GCP tweet on E2"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While its marketing materials say "up to 30% savings". The reality is much more complicated.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qGT6azPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6jcuk6vnuvb3pw34oa65.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qGT6azPz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6jcuk6vnuvb3pw34oa65.png" alt="Instance price comparision"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Or would I even dare say misleading? &lt;/p&gt;

&lt;p&gt;You see for N1/N2 instances, they receive &lt;a href="https://cloud.google.com/compute/docs/sustained-use-discounts"&gt;sustained usage discount&lt;/a&gt;. That scales between 0-to-30% when used continuously for a month. With E2 instances not having any sustained use discount (as it's built-in).&lt;/p&gt;

&lt;p&gt;So in a sustained 24/7 usage not only is the cost marginally higher, it has a much worse performance profile.&lt;/p&gt;

&lt;p&gt;And unfortunately, it is this pricing structure which makes E2, a really cool tech with little to no use case. Especially considering that the VM overall capacity is expected to suffer from an approximately 50% penalty in performance.&lt;/p&gt;

&lt;p&gt;If it's not clear, I made a table to elaborate on.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--en_y-xEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cc60najkitdd363rkjef.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--en_y-xEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/cc60najkitdd363rkjef.png" alt="E2 Usage Guidelines"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So unless you read through this whole review, and tested your application performance for it. Stick to the NX series of instances. &lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--UvxGjqye--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/imfff534vw3a6nobvbgd.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--UvxGjqye--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/imfff534vw3a6nobvbgd.jpeg" alt="Pug looking sad"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Overall Conclusion: Wasted opportunity
&lt;/h1&gt;

&lt;p&gt;Internally for Uilicious "pokemon collection of cloud providers" we currently see no use for E2. And will be sticking with our N1 instances. As our main GCP server.&lt;/p&gt;

&lt;p&gt;Despite all that, I do really look forward to the next iteration of E2, because as improvements are made to the hypervisor, and Moores law hold true. It would be about 2 more years where it outright replaces the N1 series, as the "better choice".&lt;/p&gt;

&lt;p&gt;More importantly, what this technology opens up is a new possibility. Of a future instance type (E3 ?), where one could be paying for raw CPU / ram usage directly instead. For any VM workload. Making the previous optimization (preemptible, serverless) potentially obsolete. &lt;/p&gt;

&lt;p&gt;Giving even legacy application developers a "have your cake and eat it too moment", where they can take any existing workload, and with no additional application change, with the benefit of "preemptible" instances.&lt;/p&gt;

&lt;p&gt;If Google Cloud has not realized it yet, if done correctly they can make huge wins in enterprise sales, which they desperately need (aka the people still running 20+ year old software).&lt;/p&gt;

&lt;p&gt;Till then, I will wait for GCP or another cloud provider to make such a change.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;~ Happy Testing 🖖🚀&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  About Uilicious
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://uilicious.com"&gt;Uilicious&lt;/a&gt; is a simple and robust solution for automating UI testing for web applications. Writing test scripts to validate your web platforms, can be as easy as the script below.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Which will run tests like these ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://test.uilicious.com/test/public/7t74nVS828weKMtzGgJppF"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MttsDKQE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c2kos152a1tkkdsfewkc.gif" alt="uilicious demo"&gt;Catfeeding: Uilicious testing inboxkittens XD&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://uilicious.com/pricing.html"&gt;👊 Test your own web app, with a free trial on us today&lt;/a&gt; &lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_o0KwDWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7b306yol0fp6hzt3gnt6.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_o0KwDWE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7b306yol0fp6hzt3gnt6.jpg" alt="Skeptical Baby"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  (personal opinion rant) On GCP lacking the courage to leverage on E2 tech 😡
&lt;/h1&gt;

&lt;p&gt;The whole purpose of E2 is to create a new dynamic migratable workload&lt;/p&gt;

&lt;p&gt;So why is there even a preemptible option which makes no sense in almost any scenario when compared to other preemptible options? &lt;/p&gt;

&lt;p&gt;Also isn't the very point of E2 series designed to help for long low-CPU usage workload, why does your pricing structure not favor it?&lt;/p&gt;

&lt;p&gt;These are just the tip of the iceberg on Google confusing launched messaging.&lt;/p&gt;

&lt;p&gt;If GCP removed preemptible discount option and made this new lower-performing line of VM's 30% cheaper, where it sits nicely between preemptible N1 workload, and higher performing sustained N2 workload. &lt;/p&gt;

&lt;p&gt;It would have become a serious consideration and contender. However, without doing so, it's just cool tech, desperately trying to find a use case.&lt;/p&gt;

&lt;p&gt;Sadly, and frankly, the only reason I would see why GCP is reluctant to do price cut for a new instance type is that they either&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;legitimately fear a massive price war with Amazon (which is famously known to be willing to out-bleed their competitors), &lt;/li&gt;
&lt;li&gt;fear the new lower-priced product will eat into their revenue from existing customers. &lt;/li&gt;
&lt;li&gt;Or worse, they just didn't think of it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Considering that this is the same company that made &lt;a href="https://cloud.google.com/appengine/"&gt;serverless tech in 2008&lt;/a&gt;, way ahead of any of their competitors, and not capitalize on it. There is a good chance it's the last option (deja vu?)&lt;/p&gt;

&lt;p&gt;All of this is disappointing for me to see, considering the massive amount of R&amp;amp;D and engineering resources went into making this happen. Which is a very classic google problem, really strong tech, that is disconnected from their business goals and users.&lt;/p&gt;

&lt;p&gt;Finally GCP please stop abusing the phrase "TCO", or "total cost of ownership" - we sysadmin and infrastructure personal have the tendency to think in terms of months or years (you know the server's entire potential lifespan and total cost of ownership) ... Some of us actually find it &lt;del&gt;insulting&lt;/del&gt; confusing when the term was used to imply savings when comparing to the existing long-running workload. When you actually meant to compare extremely short-lived workload instead. &lt;/p&gt;

&lt;p&gt;We actually calculate server expenses, and such misleading marketing just leads us into wasted time and effort evaluating our options and making an article on it in the process.&lt;/p&gt;

&lt;p&gt;~ Peace&lt;/p&gt;

</description>
      <category>devops</category>
      <category>serverless</category>
      <category>architecture</category>
      <category>cloud</category>
    </item>
    <item>
      <title>🤔Pop Quiz! Which of these is an infinite loop?</title>
      <dc:creator>Shi Ling</dc:creator>
      <pubDate>Wed, 23 Oct 2019 06:14:40 +0000</pubDate>
      <link>https://dev.to/uilicious/pop-quiz-which-of-these-is-an-infinite-loop-p6d</link>
      <guid>https://dev.to/uilicious/pop-quiz-which-of-these-is-an-infinite-loop-p6d</guid>
      <description>&lt;h1&gt;
  
  
  Pop Quiz!
&lt;/h1&gt;

&lt;p&gt;Which of these is an infinite loop?&lt;/p&gt;

&lt;p&gt;And guess many times console.log will be printed.&lt;/p&gt;

&lt;p&gt;A: &lt;code&gt;let&lt;/code&gt; 5x3 Loop&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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="nx"&gt;log&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="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;B: &lt;code&gt;var&lt;/code&gt; 5x3 Loop&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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="nx"&gt;log&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="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;C: &lt;code&gt;var&lt;/code&gt; 5x5 Loop&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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="nx"&gt;log&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="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;D: &lt;code&gt;let&lt;/code&gt; 5x5 Loop&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&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="nx"&gt;log&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Answer
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Which of these is an infinite loop?&lt;/strong&gt;&lt;br&gt;
B: &lt;code&gt;var&lt;/code&gt; 5x3&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Guess many times console.log will be printed.&lt;/strong&gt;&lt;br&gt;
A: &lt;code&gt;let&lt;/code&gt; 5x3 - 15 times&lt;br&gt;
B: &lt;code&gt;var&lt;/code&gt; 5x3 - Infinite times&lt;br&gt;
C: &lt;code&gt;var&lt;/code&gt; 5x5 - 5 times&lt;br&gt;
D: &lt;code&gt;let&lt;/code&gt; 5x5 - 25 times&lt;/p&gt;

&lt;p&gt;Did any of the answers surprise you? I was! &lt;/p&gt;
&lt;h1&gt;
  
  
  🧐 What, but why? The difference between &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;var&lt;/code&gt;.
&lt;/h1&gt;

&lt;p&gt;&amp;lt; flashback &amp;gt;&lt;/p&gt;



&lt;p&gt;A junior dev came up to me for help with a bug in his Javascript code that's causing a memory error, my intuition told me that there was an infinite loop somewhere. &lt;/p&gt;

&lt;p&gt;One of the nested loops stood out as a red flag to me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
    &lt;span class="c1"&gt;// some code&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 said - Hey, you're using the same variable name for outer and inner loop, this is going to cause an infinite loop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JD&lt;/strong&gt;: Huh? How come?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;:  Because the inner loop is reseting &lt;code&gt;i&lt;/code&gt; to 0, causing the outer loop to never exit. Come let's try this in the console, it's faster to just see it. &lt;/p&gt;

&lt;p&gt;To my surprise, there wasn't an infinite loop, we got this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0dz_hV8d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2ltf53ans6trtk5lvpsp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0dz_hV8d--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/2ltf53ans6trtk5lvpsp.png" alt="let 5x5 loop" width="409" height="149"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; was printed only 25 times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: Hm...? That's odd. &lt;em&gt;(Then I realised that the code uses &lt;code&gt;let&lt;/code&gt; instead of &lt;code&gt;var&lt;/code&gt;.)&lt;/em&gt; Maybe... the infinite loop only happens if you use &lt;code&gt;var&lt;/code&gt; instead of &lt;code&gt;let&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;We changed &lt;code&gt;let&lt;/code&gt; to &lt;code&gt;var&lt;/code&gt;, and ran it again:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O0Y2svvE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ymhlykebg8ckpr7m1j6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O0Y2svvE--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ymhlykebg8ckpr7m1j6m.png" alt="var 5x5 loop" width="404" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; was printed only 5 times.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Us&lt;/strong&gt;: Wait whuut? Why it is printing only 5 times?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: ... Ahhh, that's because since this example uses &lt;code&gt;var&lt;/code&gt; instead of &lt;code&gt;let&lt;/code&gt;. &lt;strong&gt;&lt;code&gt;let&lt;/code&gt; allows you to safely declare scoped variables with the same name. But when you use &lt;code&gt;var&lt;/code&gt; to declare variables with the same name in different parent-child scopes, they all reference the same thing.&lt;/strong&gt; So here, when you use &lt;code&gt;var&lt;/code&gt;, the inner loop is sharing the same &lt;code&gt;i&lt;/code&gt; as the outer loop. Meaning, when the inner loop counts &lt;code&gt;i&lt;/code&gt; up to 5 and exits, the outer loop exits immediately because it's &lt;code&gt;i&lt;/code&gt; counter is set to 5 by the inner loop.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JD&lt;/strong&gt;: Ohh... okay, then what if we set the inner loop to exit when &lt;code&gt;i&lt;/code&gt; is 3? I guess that produces an infinite loop?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: Let's find out.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ogGi_Mfp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7gbf2rygqn2jfplxr2nf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ogGi_Mfp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7gbf2rygqn2jfplxr2nf.png" alt="var 5x3 loop" width="418" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; was printed way beyond 15 times, and crashed the browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Us&lt;/strong&gt;: 🙌 We got an infinite loop!&lt;/p&gt;




&lt;p&gt;And what if we change &lt;code&gt;var&lt;/code&gt; to &lt;code&gt;let&lt;/code&gt; now?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lX31ayHp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/f27y9vjv38ih3sidx6gj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lX31ayHp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/f27y9vjv38ih3sidx6gj.png" alt="let 5x3 loop" width="430" height="162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;console.log&lt;/code&gt; was printed only 15 times.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;JD&lt;/strong&gt;: Ok, cool. But what's the use case of &lt;code&gt;var&lt;/code&gt; then?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Me&lt;/strong&gt;: Ah that's a good question. 🤔 ...none actually. There isn't a good use case for &lt;code&gt;var&lt;/code&gt; now that ES6 introduced &lt;code&gt;let&lt;/code&gt;. &lt;code&gt;var&lt;/code&gt; is just how we used to declare variables - but then there's this problem with variables leaking out of their scope - hence &lt;code&gt;let&lt;/code&gt; was proposed. Don't ask me why they decided to name the keyword &lt;code&gt;let&lt;/code&gt;. And this is why our eslint is configured to enforce &lt;code&gt;let&lt;/code&gt; over &lt;code&gt;var&lt;/code&gt;. :)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JD&lt;/strong&gt;: Oh! Ok... so if this loop isn't causing the memory error, so what's causing it? &lt;em&gt;(That turned out to be something else altogether.)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt; /flashback &amp;gt;&lt;/p&gt;

&lt;p&gt;Ah thinking back, we have it good now with ES6.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>quiz</category>
    </item>
    <item>
      <title>How do you manage deployment configs? (Especially large scale cloud agnostic ones)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Wed, 26 Jun 2019 09:40:42 +0000</pubDate>
      <link>https://dev.to/uilicious/how-do-you-manage-your-deployment-configs-especially-large-scale-cloud-agnostic-ones-358d</link>
      <guid>https://dev.to/uilicious/how-do-you-manage-your-deployment-configs-especially-large-scale-cloud-agnostic-ones-358d</guid>
      <description>&lt;h1&gt;
  
  
  Context
&lt;/h1&gt;

&lt;p&gt;For the past few weeks, I have been using a crazy mix of the following. All within a git repository (infrastructure as code), as we migrate most of our infrastructure and workload from one provider to another.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;terraform&lt;/li&gt;
&lt;li&gt;nodejs&lt;/li&gt;
&lt;li&gt;bashscripts&lt;/li&gt;
&lt;li&gt;kubernetes yaml&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With lots and lots of json, and yaml configuration files. Mostly generated from one system, to be piped to another (about 10k lines worth).&lt;/p&gt;




&lt;p&gt;&lt;code&gt;terraform&lt;/code&gt; which is commonly sold as a single solution for everything (it isnt), rapidly broke apart for us once we started doing deployments outside the usual AWS / GCP, and its poor support for kubernetes (which we use heavily)&lt;/p&gt;

&lt;p&gt;So we started patching up missing orchestration with nodejs + bashscripts.&lt;/p&gt;

&lt;p&gt;And now we have a giant soup of scripts updating other scripts configuration and applying them. Not exactly the most "elegant" solution.&lt;/p&gt;




&lt;h1&gt;
  
  
  The question : How do you do it?
&lt;/h1&gt;

&lt;p&gt;So wondering out loud, for those who do really large scale deployments, especially with a small team. &lt;/p&gt;

&lt;p&gt;Is it normal to always throw in the towel at the end, and code up a custom configuration management script to handle all this chaos? If so do you all normally do it in bash? or a custom application (like java)? or some other CLI scripting language.&lt;/p&gt;

&lt;p&gt;Alternatively, is it normal to just grow a really large sysadmin team, each managing a subset of the system?&lt;/p&gt;

&lt;p&gt;It feels like I am reinventing the wheel on these things, yet I somehow feel like there would have been a solution out there for this.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sidetrack: a large part of me just feels like redoing terraform in nodejs out of frustration, to support my use cases.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Clarification on scale
&lt;/h1&gt;

&lt;p&gt;I do believe there are multiple cloud specific offerings out there, the reason we do not use any of them is currently we run on the following list of providers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gcp&lt;/li&gt;
&lt;li&gt;aws&lt;/li&gt;
&lt;li&gt;digital ocean&lt;/li&gt;
&lt;li&gt;linode&lt;/li&gt;
&lt;li&gt;alicloud&lt;/li&gt;
&lt;li&gt;hetzner&lt;/li&gt;
&lt;li&gt;bare metal on-premise stuff&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Covering the following regions&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;singapore&lt;/li&gt;
&lt;li&gt;unitedkingdom&lt;/li&gt;
&lt;li&gt;germany-frankfurt&lt;/li&gt;
&lt;li&gt;india-bangalore&lt;/li&gt;
&lt;li&gt;canada-toronto&lt;/li&gt;
&lt;li&gt;usa-newyork&lt;/li&gt;
&lt;li&gt;usa-sanfranc&lt;/li&gt;
&lt;li&gt;netherlands-amsterdam&lt;/li&gt;
&lt;li&gt;indonesia-jarkata&lt;/li&gt;
&lt;li&gt;hongkong&lt;/li&gt;
&lt;li&gt;taiwan&lt;/li&gt;
&lt;li&gt;+3 other data centers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The complex web of providers comes in part from the need to support regions where another provider either does not exist, or does poorly performance wise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;All to run UI tests at &lt;a href="https://uilicious.com"&gt;https://uilicious.com&lt;/a&gt; !??!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>agile</category>
      <category>devops</category>
    </item>
    <item>
      <title>vue-online-prop - Am I online? A tiny VueJS plugin (&lt;1kb).</title>
      <dc:creator>Shi Ling</dc:creator>
      <pubDate>Thu, 20 Jun 2019 03:52:53 +0000</pubDate>
      <link>https://dev.to/uilicious/vue-online-prop-am-i-online-a-tiny-vuejs-plugin-1kb-61p</link>
      <guid>https://dev.to/uilicious/vue-online-prop-am-i-online-a-tiny-vuejs-plugin-1kb-61p</guid>
      <description>&lt;p&gt;I just wanted one thing only and only one thing. &lt;/p&gt;

&lt;p&gt;I just want a reactive property &lt;code&gt;$online&lt;/code&gt; in all my Vue components to tell me if the user is connected to the internet or not. &lt;/p&gt;

&lt;p&gt;I know I know, there's already a bunch of existing &lt;code&gt;vue-online&lt;/code&gt; packages, but... No thanks, I don't need the extra UI components bundled in...&lt;/p&gt;

&lt;p&gt;So I made &lt;code&gt;vue-online-prop&lt;/code&gt; (&lt;a href="https://www.npmjs.com/package/vue-online-prop" rel="noopener noreferrer"&gt;npm&lt;/a&gt;, &lt;a href="https://github.com/shiling/vue-online-prop" rel="noopener noreferrer"&gt;github&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VueOnlineProp&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;vue-online-prop&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="nx"&gt;Vue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VueOnlineProp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Usage:&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;div&lt;/span&gt; &lt;span class="na"&gt;v-if=&lt;/span&gt;&lt;span class="s"&gt;"!$online"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
    You are currently offline!
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tada.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53del7kraqs4ct2u4qnw.gif" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F53del7kraqs4ct2u4qnw.gif" alt="I added a offline indicator!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's all!&lt;/p&gt;




&lt;h3&gt;
  
  
  (Extras) How it is made
&lt;/h3&gt;

&lt;h5&gt;
  
  
  How to check if I am online with Javascript
&lt;/h5&gt;

&lt;p&gt;You can query the internet connectivity through Javascript using &lt;code&gt;navigator.onLine&lt;/code&gt;, and listen to changes to the connectivity using the &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;offline&lt;/code&gt; events on the window.&lt;/p&gt;

&lt;p&gt;👉&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine" rel="noopener noreferrer"&gt;MDN docs on &lt;code&gt;navigator.onLine&lt;/code&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Creating the VueJS plugin
&lt;/h5&gt;

&lt;p&gt;This plugin simply listens to the &lt;code&gt;online&lt;/code&gt; and &lt;code&gt;offline&lt;/code&gt; events on the window, and sets the value of &lt;code&gt;navigator.onLine&lt;/code&gt; to a reactive property &lt;code&gt;status&lt;/code&gt; managed by the plugin. When the plugin is installed using &lt;code&gt;Vue.use(VueOnlineProp)&lt;/code&gt;, it adds a &lt;code&gt;beforeCreate&lt;/code&gt; mixin, which will to bind the reactive property &lt;code&gt;status&lt;/code&gt; to the &lt;code&gt;$online&lt;/code&gt; property in every component. (👉&lt;a href="https://github.com/shiling/vue-online-prop/blob/master/index.js" rel="noopener noreferrer"&gt;Here's the code&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>vue</category>
      <category>ux</category>
      <category>showdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Are we "developers" gatekeeping "knowledge" from our juniors and peers? 🤦</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Wed, 05 Jun 2019 15:14:25 +0000</pubDate>
      <link>https://dev.to/uilicious/are-we-developers-gatekeeping-knowledge-from-our-juniors-and-peers-4gc6</link>
      <guid>https://dev.to/uilicious/are-we-developers-gatekeeping-knowledge-from-our-juniors-and-peers-4gc6</guid>
      <description>&lt;h1&gt;
  
  
  Background Context
&lt;/h1&gt;

&lt;p&gt;My startup co-founder recently wrote an article on what I previously considered "common knowledge", an article which went "viral". &lt;/p&gt;

&lt;p&gt;Unfortunately, as per the rules of the internet. Anything "viral" will draw a fair bit of criticism and even personal attacks, especially outside dev.to; Some which I saw hurt my co-founder personally, as we look through them.&lt;/p&gt;

&lt;p&gt;However, this is not about sexism, javascript, nor the internet rule (all which has valid problems worth highlighting separately).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is about "developers", and our role in gatekeeping "knowledge" like the following from others&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/shiling" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XxasGPxV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Fp32YymT--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/11766/4a059374-4b4f-4777-bf0a-8a0301476298.png" alt="shiling image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Javascript Array.push is 945x faster than Array.concat 🤯🤔&lt;/h2&gt;
      &lt;h3&gt;Shi Ling ・ May  2 '19 ・ 8 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#webperf&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;Personal attacks aside, what caught my attention, that drove me to write this article - was the following comment:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Isn't it obvious that ________________ are you a _____, &lt;br&gt;
&lt;strong&gt;Never ever write such [ ] articles again.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
Side note: As its not my intention to shame/attribute the original author. The quote is paraphrased to make it un-googleable and to remove expletives. To be clear, the exact wording is different



&lt;p&gt;It struck me not only because of how cruel it was at the end - but how the statement, was something I was guilty of saying as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;SL: Really?! &lt;code&gt;push&lt;/code&gt; made such a big difference&lt;/li&gt;
&lt;li&gt;EU: Well &lt;code&gt;concat&lt;/code&gt; needs to create a new array, and copy everything&lt;/li&gt;
&lt;li&gt;SL: Doesn't the javascript jvm, automatically optimize such execution? Seeing that I do not use the old array&lt;/li&gt;
&lt;li&gt;EU: Well it doesn't, and unfortunately due to the spec ...&lt;/li&gt;
&lt;li&gt;SL: I should write an article on this&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;EU: Isn't it very obvious, I doubt it's worth writing&lt;/strong&gt; &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SL: It wasn't obvious to me&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;EU: (in my head - I am surprised you didn't know)&lt;/li&gt;
&lt;li&gt;EU: Ok, it's worth a try then
SL stands for Shi Ling, my co-founder. While EU represents me, Eugene
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While it's much lighter in tone, the line of reasoning to not write the article is the same.&lt;/p&gt;

&lt;p&gt;In the position of seniority, even lightly brushing off of such content being written can have chilling effects on other junior developers and peers. Especially in a much larger organization. So while such statements may not have deterred Shi Ling (she wrote it in the end), it could however for the rest of my peers around me silence them.&lt;/p&gt;

&lt;p&gt;And was I wrong in my criticism!&lt;/p&gt;

&lt;p&gt;Looking at the number of positive retweets and comments on how "I need to rewrite my code", or "I never knew", occurred. It wasn't obvious, and it is an article worth writing.&lt;/p&gt;

&lt;p&gt;However, what hit me really hard - was when out of "frustration" or immaturity. I regrettably looked into the commenter's profile. &lt;/p&gt;

&lt;p&gt;I realized it wasn't just a simple careless statement by a young teenager. &lt;/p&gt;

&lt;p&gt;It was from a well respectable senior engineer, in charge of a large team of developers, with a similar background to me. To me, it was terrifying.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I was looking into the mirror, of what could be me in the future&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--P1O1wRxS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a21inheg22a2vblosf1z.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--P1O1wRxS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/a21inheg22a2vblosf1z.jpg" alt="The evil in the mirror"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And on further digging around the various comments, he was not the only one...&lt;/p&gt;

&lt;p&gt;Which brings me to my next point...&lt;/p&gt;




&lt;h1&gt;
  
  
  PSA: What is common to you, may not be to another developer. So think twice before suppressing such content.
&lt;/h1&gt;

&lt;p&gt;As an industry, programming is relatively immature, of being less then 200 years old (starting from Ada Lovelace). The web industry is even younger at being under 30 years old.&lt;/p&gt;

&lt;p&gt;This pales drastically to&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scientific medicine: ~2500 years old&lt;/li&gt;
&lt;li&gt;architecture and construction: &amp;gt;5000 years old&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a result, the technology we learn seems to be constantly changing, with little in the way of a stable (think 12+ years) long term standard for anything.&lt;/p&gt;

&lt;p&gt;By this definition: any assumptions you made on what is learned for your generation as "common knowledge" that isn't worth spreading, is a false assumption made to your junior and peers.&lt;/p&gt;

&lt;p&gt;We should also kick out the notation that, if one does not "understand this", that individual is not deserving to be called a developer. &lt;/p&gt;

&lt;p&gt;To clarify, my co-founder is by no means a "junior" developer. We both have very very different specializations. &lt;/p&gt;

&lt;p&gt;While I may have really deep dive knowledge on the ES5 spec, and how V8 works internally, which distorted my view on what's considered "common knowledge" for javascript.&lt;/p&gt;

&lt;p&gt;Inversely, she has really deep encyclopedia-like knowledge on the various quirks of CSS, and how modern front end frameworks like &lt;code&gt;vue.js&lt;/code&gt; works. Something which I admittedly still make very fundamental novice mistakes till today.&lt;/p&gt;

&lt;p&gt;So if my assumptions are wrong to a fellow "senior" developer with different specialization, wouldn't it be worse for a new "junior" developer. Especially with the rise of coding boot camps.&lt;/p&gt;

&lt;p&gt;So until we can figure out a common stable universal engineering standard for programming, which we can assume as "common knowledge", like how architects or doctors do (which we probably will not for another 50 years). I am heavily ringing this bell now, as we are like no other industry out there.&lt;/p&gt;

&lt;p&gt;Never make that assumption.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--MGWaYUjr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q0t9nzpa812aflac0rq5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--MGWaYUjr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/q0t9nzpa812aflac0rq5.jpg" alt="Heraclitus quote: There is nothing permanent except change"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Heck, to the unconvinced old seniors. Many of us (including me) used to consider C style memory pointer manipulation as essential common knowledge, which in absence, we used as grounds for a "no hire" for juniors.&lt;/p&gt;

&lt;p&gt;How wrong are we now in modern web development and the programming language they use?&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Bro-grammer? Couldn't we be kinder?
&lt;/h1&gt;

&lt;p&gt;Even with valid criticism...&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Ask yourself, would you have possibly made the same mistake?&lt;/li&gt;
&lt;li&gt;Would you like to have such things said to you, or your loved ones?&lt;/li&gt;
&lt;li&gt;Is it constructive criticism?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Instead of gatekeeping what should be written or not written on "superior grounds". We could instead encourage improvement with our criticism.&lt;/p&gt;

&lt;p&gt;For example, one repeated criticism. Is regarding benchmarking methodology. However instead of statements like.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Worst benchmarking ever&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A kinder, more constructive comment would be&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We could improve the benchmark by doing ____ instead of ____&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And to say it upfront, for most of the constructive criticism on benchmarking, you were right. While it probably will not change the end result, due to the large difference in between. It is a valid area of improvement, and both my co-founder and I do thank you for that.&lt;/p&gt;

&lt;p&gt;Or as XKCD lovingly put it&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5TYtxE_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ha6olaye4p1bhxumo6qa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5TYtxE_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ha6olaye4p1bhxumo6qa.png" alt="XKCD #1053"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Saying 'what kind of an idiot doesn't know about the Yellowstone supervolcano' is so much more boring than telling someone about the Yellowstone supervolcano for the first time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;So which would you rather be?&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Bro-grammer?
&lt;/h1&gt;

&lt;p&gt;When I said this is not about sexism, I mean it.&lt;/p&gt;

&lt;p&gt;The suppression of knowledge probably happens regardless of gender. It definitely happens from senior to junior as well.&lt;/p&gt;

&lt;p&gt;However, the phrase "Bro-grammer" is used intentionally. &lt;/p&gt;

&lt;p&gt;I am sure there are exceptions (there always is), but this is more on us the majority, the male gender of the development community, then the other way.&lt;/p&gt;

&lt;p&gt;And while I will not have any statistical numbers to back it up. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The minority groups within our community know very well how it feels like to have their voice heavily suppressed.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;em&gt;The minority groups are the ones most mindful on not gatekeeping "knowledge" from others.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For almost all the personal attacks / mean criticism I seen to my co-founder was from our side of the male development community.&lt;/p&gt;

&lt;p&gt;And it's on us to improve ourselves on this, to improve our community. Consciously, in speaking out against it. Or sub-consciously in our words and actions.&lt;/p&gt;

&lt;p&gt;I am not asking each one of us to achieve an unreasonable perfectionist standard (as there will be slips of tounges), nor am I criticizing of who you are now. &lt;/p&gt;

&lt;p&gt;I am saying this, as someone who has made mistakes as well. To make small incremental personal, mindful changes in us individuals that will allow us to push the community forward.&lt;/p&gt;




&lt;h1&gt;
  
  
  So ask yourself the man in the mirror? Will you make that change?
&lt;/h1&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/PivWY9wn5ps"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To improve the community around you. In encouraging the sharing of knowledge (as trivial as it may seem to you, or even me). Or to share such knowledge yourself.&lt;/p&gt;




&lt;h1&gt;
  
  
  I have seen similar things happen to other articles/tweets, anything I can do to help?
&lt;/h1&gt;

&lt;p&gt;Instead of fighting head on, and "feeding the trolls".&lt;/p&gt;

&lt;p&gt;In cases of unconstructive criticism (eg. worse benchmark), in place of the author, asked politely &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What do you mean by _____, how could the content be improved on?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This gives the individual, a chance to clarify as they may not always mean harm intentionally, sometimes it could simply be words being lost in translation.&lt;/p&gt;

&lt;p&gt;Just keep in mind to remain &lt;strong&gt;respectful, constructive and clear&lt;/strong&gt; to defuse any tension&lt;/p&gt;

&lt;p&gt;For really harsh comments or personal attacks, it might be better to reply to the comment, or author, while ignoring the bully. To let them know that there are others who appreciated their work. While lightly pointing at the criticism.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Personally I found the content useful, regardless of what anyone else says. Thank you&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The above is universal regardless of content type (it can be applied outside of the development community).&lt;/p&gt;

&lt;p&gt;And finally, for the developer who engages in such criticism, and partly the reason I wrote this article - is so I can point to it in the future for others. As it happens to them. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Personally I think what you are doing is great, regardless of what anyone else says. Thank you. And for the developers who say otherwise, we should be encouraging instead of suppressing our peers.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/picocreator" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--odgNY3b7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--lnJT1GdY--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/56734/7c96ac1b-1775-452b-8aa2-e49cac86d45b.jpeg" alt="picocreator image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/uilicious/are-we-developers-gatekeeping-knowledge-from-our-juniors-and-peers-4gc6" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Are we "developers" gatekeeping "knowledge" from our juniors and peers? 🤦&lt;/h2&gt;
      &lt;h3&gt;Eugene Cheah ・ Jun  5 '19 ・ 8 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#discuss&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#motivation&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;

&lt;/blockquote&gt;

&lt;p&gt;Keep in mind while doing any of the above, as you run the risk of diverting the critical comment from the author and onto yourself. If it gets personal on you, do not feed into it. Be strong and ignore it.&lt;/p&gt;

&lt;p&gt;Know you have done a part in letting the content creator, know that the criticism is not the only voice there. And as an observer to the receiving end, even if they end up in silence, I will say it helps go a long way in knowing there was someone there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For all the unsung heroes, who did so. Thank you.&lt;/strong&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Keep doing your best!
&lt;/h1&gt;

&lt;p&gt;To my co-founder, even if you face a double standard in your work previously, or the triple standards in investors as you fundraise presently, or the cruelty of the internet as you write content.&lt;/p&gt;

&lt;p&gt;To all the other working moms, single moms, LGBTQ, and other minority groups.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;keep on coding &lt;/li&gt;
&lt;li&gt;keep on improving&lt;/li&gt;
&lt;li&gt;keep on writing&lt;/li&gt;
&lt;li&gt;keep on doing, the various great things you are working on&lt;/li&gt;
&lt;li&gt;do not let the criticism suppress you&lt;/li&gt;
&lt;li&gt;push that glass ceiling&lt;/li&gt;
&lt;li&gt;and be great at what you do&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Peace 🖖&lt;/p&gt;




&lt;h1&gt;
  
  
  Additional advice from comments
&lt;/h1&gt;

&lt;p&gt;Also, consider reading up on &lt;a href="https://www.recurse.com/social-rules"&gt;recurse.com/social-rules&lt;/a&gt;. &lt;/p&gt;


&lt;div class="liquid-comment"&gt;
    &lt;div class="details"&gt;
      &lt;a href="/downey"&gt;
        &lt;img class="profile-pic" src="https://res.cloudinary.com/practicaldev/image/fetch/s--QFJhW40M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--LTSCyk9P--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_50%2Cq_auto%2Cw_50/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/170552/797597da-2c9d-4fae-9ca6-b56639e40665.jpeg" alt="downey profile image"&gt;
      &lt;/a&gt;
      &lt;a href="/downey"&gt;
        &lt;span class="comment-username"&gt;Tim Downey&lt;/span&gt;
      &lt;/a&gt;
      &lt;span class="color-base-30 px-2"&gt;•&lt;/span&gt;
&lt;a href="https://dev.to/downey/comment/bg1d" class="comment-date crayons-link crayons-link--secondary fs-s"&gt;
  &lt;time&gt;
    Jun  6 '19
  &lt;/time&gt;
&lt;/a&gt;

    &lt;/div&gt;
    &lt;div class="body"&gt;
      

&lt;p&gt;Thanks for writing up this post &lt;a class="comment-mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;
!&lt;/p&gt;

&lt;p&gt;Along these lines, I am a big fan of the Recurse Center's &lt;a href="https://www.recurse.com/social-rules"&gt;social rules&lt;/a&gt; -- particularly in this case the one about "no feigning surprise." Their explanation of this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Feigned surprise is when you act surprised when someone doesn’t know something. Responding with surprise in this situation makes people feel bad for not knowing things and less likely to ask questions in the future, which makes it harder for them to learn.&lt;/p&gt;

&lt;p&gt;No feigning surprise isn’t a great name. When someone acts surprised when you don’t know something, it doesn’t matter whether they’re pretending to be surprised or actually surprised. The effect is the same: the next time you have a question, you’re more likely to keep your mouth shut. An accurate name for this rule would be no acting surprised when someone doesn’t know something, but it’s a mouthful, and at this point, the current name has stuck.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Feigning (or actually being) surprised is definitely not as mean spirited as some of the examples you referenced, but it can still discourage folks that are new to an area from speaking up or reaching out for help. Just wanted to note this as well since I think it's pretty easy for (otherwise well-intentioned) people to do this without meaning to.&lt;/p&gt;



    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;And understand the difference between passive/active gate-keeping&lt;/p&gt;


&lt;div class="liquid-comment"&gt;
    &lt;div class="details"&gt;
      &lt;a href="/kayis"&gt;
        &lt;img class="profile-pic" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ua43TEXq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Dya1ZKgv--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_50%2Cq_auto%2Cw_50/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/1106/c30ab362-496a-44c4-bcf5-0d4747afd39e.jpg" alt="kayis profile image"&gt;
      &lt;/a&gt;
      &lt;a href="/kayis"&gt;
        &lt;span class="comment-username"&gt;K&lt;/span&gt;
      &lt;/a&gt;
      &lt;span class="color-base-30 px-2"&gt;•&lt;/span&gt;
&lt;a href="https://dev.to/kayis/comment/bfjj" class="comment-date crayons-link crayons-link--secondary fs-s"&gt;
  &lt;time&gt;
    Jun  5 '19
  &lt;/time&gt;
&lt;/a&gt;

    &lt;/div&gt;
    &lt;div class="body"&gt;
      

&lt;p&gt;I think the problem is two-fold.&lt;/p&gt;

&lt;p&gt;One part is the active gate-keeping. People running around telling everyone they or the tools they use aren't good enough.&lt;/p&gt;

&lt;p&gt;How can you use PHP when there is Java? How Java when there is C#? Why switch to JavaScript when PHP finally became a &lt;em&gt;real&lt;/em&gt; programming language?&lt;/p&gt;

&lt;p&gt;Even my programming techniques professor said JavaScript was a toy and nothing compared to Java.&lt;/p&gt;

&lt;p&gt;People shitting on each others' work for all kind of senseless reasons instead of helping each other out. That needs to stop and I have the feeling that more women and minorities in the industry will just lead to that. CoCs are spreading like a wildfire and people become generally kinder. The few old-school uber-nerds I saw here on dev.to didn't stay for long.&lt;/p&gt;

&lt;p&gt;On the other hand, we have passive gate-keeping or simply ignorance. I guess that is what you're talking about. This can have many reasons and I can only talk about the ones I encountered.&lt;/p&gt;

&lt;p&gt;I often had the impression I'm a bad-to-mediocre developer, so I didn't value most of the stuff I knew and this lead me to think I was one of the last in the industry to learn stuff. Took me a year to understand functions, took me a few months to understand closures, took me another year to understand monads, etc. I was always a slow learner, swimming with 9, biking with 14, so I guess that's where the impression came from that everyone else already knew the stuff I just learned.&lt;/p&gt;

&lt;p&gt;I also had to work with many of such old-school uber-nerds who choose tech for projects and generally acted like they had it all figured out. Took me a few years of freelancing and blogging to find out most of them are just talking big and there are a whole bunch of people out there who don't know half as much as I do and would love to work with or learn from me.&lt;/p&gt;



    &lt;/div&gt;
&lt;/div&gt;





&lt;h1&gt;
  
  
  History sidetrack
&lt;/h1&gt;

&lt;p&gt;For those interested in the accuracy of the yearly figures used above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"&gt;1991 : HTTP protocol 0.9 was formalised&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://en.wikipedia.org/wiki/Portable_computer"&gt;1975 : The rise of portable computer such as SCAMP / IBM 5100&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;1958 : &lt;a href="https://en.wikipedia.org/wiki/Fortran"&gt;Fortran&lt;/a&gt;/&lt;a href="https://en.wikipedia.org/wiki/Lisp_(programming_language)"&gt;LISP&lt;/a&gt;, the predecessor to most modern programming languages.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/History_of_programming_languages"&gt;1842 : Ada Lovelace, published the first known computer program&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Herophilos"&gt;300 BC : Herophilos, the first greek anatomist to push scientific based medicine&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Megalithic_Temples_of_Malta"&gt;3600 BC : Megalithic Temples of Malta, the oldest known free-standing structure on earth, ie not carved into a mountain&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;blockquote&gt;
&lt;p&gt;I have been sitting on this piece for some time, it is intentionally written in a way to not blame individuals for their past action, but to encourage them to reflect back on how to do better.&lt;/p&gt;

&lt;p&gt;As writing is hardly my biggest strength, and I am sure my approach to this topic may be flawed as well. Any discussion, for similar experiences on this issue, either to encourage others, or to suggest other possible ideas, or even amendments that should be made to this article. Will be gladly appreciated.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>discuss</category>
      <category>beginners</category>
      <category>motivation</category>
    </item>
    <item>
      <title>⚙️ Explain Selenium &amp; Webdrivers automation (Like I'm Five)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Wed, 15 May 2019 07:42:31 +0000</pubDate>
      <link>https://dev.to/uilicious/explain-selenium-webdrivers-like-i-m-five-16ng</link>
      <guid>https://dev.to/uilicious/explain-selenium-webdrivers-like-i-m-five-16ng</guid>
      <description>&lt;p&gt;This was originally drafted as an answer to the question ...&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/edwinthinks" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&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%2Fuser%2Fprofile_image%2F141569%2F9a509ae3-6266-4deb-a28c-714067d75ac2.jpeg" alt="edwinthinks"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/edwinthinks/explain-selenium-webdrivers-like-i-m-five-24me" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Explain Selenium &amp;amp; Webdrivers Like I'm Five&lt;/h2&gt;
      &lt;h3&gt;Edwin Mak ・ May 12 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#explainlikeimfive&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#selenium&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#e2e&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;That grew into an entire post on its own 😅 (I talk too much)&lt;/p&gt;

&lt;p&gt;As I'm the co-founder of &lt;a href="https://uilicious.com/" rel="noopener noreferrer"&gt;Uilicious.com&lt;/a&gt; - a full end-to-end testing platform built on webdriver servers. This post is meant to be a series of key facts I wish I knew at the start of the "webdriver" learning journey.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Oneliner to explaining Selenium / Webdriver&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Automation standard, to control all your web browsers via an API.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Webdriver Specification vs Selenium Implementation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Think along the lines of HTML5 vs Chrome&lt;/li&gt;
&lt;li&gt;Webdriver refers to the specification&lt;/li&gt;
&lt;li&gt;Selenium refers to an opensource implementation of the specification. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Selenium is currently the most prominent implementation of webdriver, with several additional commands in selenium that are not part of the webdriver specification. Causing confusion on the distinction in between.&lt;/p&gt;

&lt;p&gt;This is particularly important, to debug discrepancies in both documentation, for every browser. &lt;/p&gt;

&lt;p&gt;However as they are used extremely interchangeably, it can get incredibly confusing in guides and tutorials. This is partially due to their mixed history together. &lt;/p&gt;

&lt;p&gt;Understanding this difference will probably save you hours of documentation pain down the line.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Few9n9yw9l63bkk3xbd2g.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Few9n9yw9l63bkk3xbd2g.jpg" alt="Every infant is a miracle"&gt;&lt;/a&gt;&lt;/p&gt;
Every child is a miracle



&lt;p&gt;&lt;strong&gt;Webdriver Specification is a mini-miracle on its own&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For those who have done work across multiple browsers, especially in the earlier years (10+ years ago). You will know how much of an impossibility it was to get a group of browser vendors, who are competitors to each other, agree on something together.&lt;/p&gt;

&lt;p&gt;The problem for webdriver, is that it's made worse by being a relatively "low priority" item, with resource constraints. Being a feature that will never be experienced directly by the end users. &lt;/p&gt;

&lt;p&gt;With every browser only having a single to a handful of developers working on their webdriver implementation.&lt;/p&gt;

&lt;p&gt;The fact that we have a somewhat working protocol alone, in w3c, is a small miracle from the many years of hard work from the Selenium development team.&lt;/p&gt;

&lt;p&gt;It may be at times incomplete and inconsistent among browsers, however, it is a spec that we can at least somewhat agree on, and be proud of since it started formalizing in 2011.&lt;/p&gt;

&lt;p&gt;So a small round of applause for them 👏&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I may be deeply critical, I do however greatly respect what they have done so far.&lt;/p&gt;
&lt;/blockquote&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F74vt3vwrj5kiqpqvszlq.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F74vt3vwrj5kiqpqvszlq.jpg" alt="Choose a flavour"&gt;&lt;/a&gt;&lt;/p&gt;
Choose any flavour



&lt;p&gt;&lt;strong&gt;Webdriver is a HTTP API, with many client-side library implementations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can choose your language/poison of choice here&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP API: &lt;a href="https://w3c.github.io/webdriver/" rel="noopener noreferrer"&gt;W3C Specification&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Javascript: &lt;a href="https://webdriver.io/" rel="noopener noreferrer"&gt;webdriver.io&lt;/a&gt; (this is confusing as they use the same name)&lt;/li&gt;
&lt;li&gt;PHP: &lt;a href="https://github.com/facebook/php-webdriver" rel="noopener noreferrer"&gt;php-webdriver&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Ruby: &lt;a href="https://metacpan.org/pod/Selenium::Remote::Driver" rel="noopener noreferrer"&gt;Selenium::Remote::Driver&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One side advice, like the early days of CSS/JS on multiple browsers, learn to distrust the specifications compatibility between different browsers/clients. Documentation is inconsistent and all over the place.&lt;/p&gt;

&lt;p&gt;While there is &lt;a href="https://developer.mozilla.org/en-US/" rel="noopener noreferrer"&gt;MDN mozilla.org&lt;/a&gt; that will happily tell you what works (or not) on which browsers, for CSS/JS.&lt;/p&gt;

&lt;p&gt;Webdriver as of now do not have an equivalent online resource. Internally this is something we may plan to spin out on our own in Uilicious, considering we are starting to pile up a mountain of random post-it notes on how the protocol differs for each browser.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Webdriver is a "Remote Control Interface"&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The phrase "Remote Control Interface" is the exact quote taken from the specification.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Webdriver is built as an automation protocol, making it less than ideal for test automation as they have very different objectives. For example, webdriver has very limited assertion commands.&lt;/p&gt;

&lt;p&gt;To use webdriver for testing, most users will use a testing solution, which extends the commands for end-to-end testing.&lt;/p&gt;

&lt;p&gt;For example, to check if a text exists on screen in webdriver, you will need to run the following commands.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;find the element where you expect the text to be&lt;/li&gt;
&lt;li&gt;get the element and its inner HTML text string&lt;/li&gt;
&lt;li&gt;check if text exists in the result text string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For uilicious (and a few other test solutions) we simplify it as a single command&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;I.see("Text You Expect to see on screen")&lt;/code&gt;&lt;/li&gt;
&lt;/ul&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1idfrhdqzixqm9hi3luv.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1idfrhdqzixqm9hi3luv.jpg" alt="Mac mini farm"&gt;&lt;/a&gt;&lt;/p&gt;
Need to run an army of safari tests? You may need one of these



&lt;p&gt;&lt;strong&gt;Webdriver Server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://www.npmjs.com/package/selenium-standalone" rel="noopener noreferrer"&gt;Selenium-standalone&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Typically a webdriver server refers to a single VM / Physical instance of the desired OS of choice, with the "webdriver server" program installed. &lt;/p&gt;

&lt;p&gt;Alternatively, this could include mobile devices attached to the webdriver serve, typically by a USB cable.&lt;/p&gt;

&lt;p&gt;This will then run typically up to 1 session at a time, on a single browser. Where the automation will take place from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webdriver Grid/Router&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example: &lt;a href="https://www.seleniumhq.org/docs/07_selenium_grid.jsp" rel="noopener noreferrer"&gt;Selenium-grid&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A dedicated server program, which connects multiple webdriver servers together for use on a single API endpoint. This is used to get around the limitations that each server will have, and run multiple sessions on a larger scale. &lt;/p&gt;

&lt;p&gt;Along with user authentication, and limitations for access to the grid (to prevent any one user from running too many tests at a time)&lt;/p&gt;

&lt;p&gt;Cloud providers for webdriver, Saucelab and browserstack provides such endpoints with devices as old as blackberries. Alongside the latest nightly build of chrome.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc268hibnopp6hwd3a4pw.jpg" 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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fc268hibnopp6hwd3a4pw.jpg" alt="Can you hear me from the other side?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Networking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One common analogy we use in the office, is to think of uilicious commands as instructions we give over the phone. To the operator on the other side being the webdriver server. Executing commands such as &lt;code&gt;I.goTo&lt;/code&gt;, &lt;code&gt;I.see&lt;/code&gt;, and &lt;code&gt;I.click&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;So from a networking endpoint, to test any website - the webdriver server on the other side must be able to load the given testing URL.&lt;/p&gt;

&lt;p&gt;Likewise, just as how someone on the other side of the phone is unable to connect to a website hosted on your laptop on localhost directly. Neither can the webdriver server.&lt;/p&gt;

&lt;p&gt;However in a similar situation, you are able to provide access through the usage of other tools such as &lt;a href="https://ngrok.com/" rel="noopener noreferrer"&gt;ngrok&lt;/a&gt;, or port forwarding.&lt;/p&gt;




&lt;blockquote&gt;
&lt;p&gt;Hopefully that's enough key pointers for anyone new to webdriver who wish to dive deeply into it 🙂&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  So about uilicious, which is built on webdriver?
&lt;/h1&gt;

&lt;p&gt;What I work on in uilicious, is to run test scripts like these&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lets go to dev.to&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Fill up search&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search&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;uilicious&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// I should see myself or my co-founder&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shi Ling&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eugene Cheah&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;And churn out &lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE" rel="noopener noreferrer"&gt;sharable test result&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE" rel="noopener noreferrer"&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%2Fnreaiqlqnokx20xn8zym.gif" alt="Uilicious Snippet dev.to test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Like the above. All through a fully managed cloud platform. &lt;a href="https://uilicious.com" rel="noopener noreferrer"&gt;https://uilicious.com&lt;/a&gt;&lt;br&gt;
(Go give it a try 😉)&lt;/p&gt;




&lt;h1&gt;
  
  
  Happy Shipping 🖖🏼🚀
&lt;/h1&gt;

</description>
      <category>explainlikeimfive</category>
      <category>selenium</category>
      <category>e2e</category>
      <category>beginners</category>
    </item>
    <item>
      <title>😼 KittenRouter: Opensource 🔍 Elasticsearch Monitoring, Logging &amp; 🚦 Failover of webtraffic (with ☁️ Cloudflare Workers)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Tue, 14 May 2019 14:11:24 +0000</pubDate>
      <link>https://dev.to/uilicious/elasticsearch-monitoring-logging-failover-of-webtraffic-using-opensource-kittenrouter-with-cloudflare-workers-4bf3</link>
      <guid>https://dev.to/uilicious/elasticsearch-monitoring-logging-failover-of-webtraffic-using-opensource-kittenrouter-with-cloudflare-workers-4bf3</guid>
      <description>&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1j3qpkan01o7c2n9ugg1.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F1j3qpkan01o7c2n9ugg1.png" alt="KittenRouter logo"&gt;&lt;/a&gt;&lt;/p&gt;
Special thanks to Nai Jie for the logo (he also did InboxKitten logo)



&lt;h1&gt;
  
  
  Introducing KittenRouter - Serverless cousins to InboxKitten
&lt;/h1&gt;

&lt;p&gt;Since we were using Cloudflare serverless kittens for our API. Why not expand this for the UI assets as well?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Log every web request on cloudflare (This is an &lt;a href="https://support.cloudflare.com/hc/en-us/articles/216672448-Cloudflare-Logs-formerly-ELS-" rel="noopener noreferrer"&gt;enterprise only feature&lt;/a&gt;), helping us to track server failures.&lt;/li&gt;
&lt;li&gt;Automated failover on server errors, to maintain overall site availability (High Availability!).&lt;/li&gt;
&lt;li&gt;All as a reusable class module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keeping the project small along the &lt;a href="https://en.wikipedia.org/wiki/Unix_philosophy" rel="noopener noreferrer"&gt;"do one thing and do it well"&lt;/a&gt; concept, the pseudo code could be summarized as below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;when receiving a request &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; each backend configuration &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;invalid backend configuration - wrong domain, etc&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;continue&lt;/span&gt; - to next backend configuration iteration
      &lt;span class="o"&gt;}&lt;/span&gt;
      perform cachable request to the backend
      log request to ElasticSearch backend - or alternative log provider
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; 
         request result is valid &lt;span class="o"&gt;||&lt;/span&gt; 
         is last configuration &lt;span class="o"&gt;||&lt;/span&gt; 
         default backend configuration
      &lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
         &lt;span class="k"&gt;return &lt;/span&gt;result - Ends request processing
      &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt; - to next backend configuration iteration
   &lt;span class="o"&gt;}&lt;/span&gt;      
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;# PS: The above is conceptually the synchronous flow. In the actual&lt;/span&gt;
&lt;span class="c"&gt;# implementation, logging should occur asynchronously without blocking&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This allows us to configure redundancies within the backend, with commons.host servers coming in first, followed by firebase as "backup".&lt;/p&gt;


&lt;h1&gt;
  
  
  Background context (why we made this?)
&lt;/h1&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/uilicious" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&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%2Forganization%2Fprofile_image%2F182%2F2f087e13-6b3f-4089-b781-a01543a38062.png" alt="Uilicious"&gt;
      &lt;div class="ltag__link__user__pic"&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%2Fuser%2Fprofile_image%2F56734%2F7c96ac1b-1775-452b-8aa2-e49cac86d45b.jpeg" alt=""&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/uilicious/why-we-migrated-serverless-inboxkitten-from-firebase-to-cloudflare-workers-and-commonshost-gd3" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Why we migrated opensource 😼inboxkitten (77 million serverless request) from 🔥Firebase to ☁️Cloudflare workers &amp;amp; 🐑CommonsHost&lt;/h2&gt;
      &lt;h3&gt;Eugene Cheah for Uilicious ・ Mar 27 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#showdev&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#whywe&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;p&gt;When we migrated our UI frontend from Firebase to &lt;a href="https://commons.host/" rel="noopener noreferrer"&gt;Commons.Host (an opensource and public CDN/Static hosting providers)&lt;/a&gt;, one of the issues we faced was random 404's in random remote locations. Like Russia - possibly due to the lack of servers there.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpy4xba5ennrfjt3phh9.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fmpy4xba5ennrfjt3phh9.png" alt="Commonshost Global Map"&gt;&lt;/a&gt; &lt;/p&gt;
Commons.host, server locations at point of writing



&lt;p&gt;And because we are supporters of the growing 🇸🇬 Commons Host project, we decided to stand by them together, instead of ditching them for alternatives.&lt;/p&gt;

&lt;p&gt;We wanted a better way to debug such issues while improving the overall reliability of our service.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Side note to any big-brother authorities, we only track static assets loading with IP address masking on inboxkitten.com. No logging is done on the API. So yea, do not bother asking me for such data.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Elasticsearch Monitoring: Commons Host
&lt;/h1&gt;

&lt;p&gt;What's awesome, is using the logging done by KittenRouter, of our commons host deployment, we can start having hard data on its status.&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fui2rilz7vuv581xg1qm8.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fui2rilz7vuv581xg1qm8.png" alt="HTTP code logging"&gt;&lt;/a&gt;&lt;/p&gt;
78 errors out of 12,069 requests ~ 0.65% failure rate



&lt;p&gt;Is that currently in such a setup, we have 99.35% of our traffic routed through the free public servers of commons.host. &lt;/p&gt;

&lt;p&gt;And for the remaining 0.65% of request which fails, They automatically fall back onto firebase infrastructure, under its free tier.&lt;/p&gt;

&lt;p&gt;While it's not yet 9 nines, considering that these commons.host servers and cloudflare servers are scattered around the world. &lt;a href="https://www.cdnperf.com/#!rum" rel="noopener noreferrer"&gt;It's well within the 98% of real world user metrics of most CDNs&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4f3n8js8c6mzd2vlhwgb.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F4f3n8js8c6mzd2vlhwgb.png" alt="Commons host dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, with the header information provided, we can keep track of the error rate on a per server basis. And with the KittenRouter setup on Cloudflare, you can configure such monitoring for any existing site without application code change.&lt;/p&gt;

&lt;p&gt;We have since provided the commons.host team access to the above elasticsearch cluster for extracting useful data on their infrastructure under real user load.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Clarification notes, for privacy reasons, commons.host do not perform such level of logging on their systems for sites hosted on their platform. &lt;/p&gt;

&lt;p&gt;The intention here for us at Uilicious, is to provide them with useful real-world usage metrics of their servers, and effectively exempting our website, inboxkitten.com, from such privacy protection, to help them improve their service.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Elasticsearch Monitoring: InboxKitten
&lt;/h1&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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx54ehgmxsfkkicfke089.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%2Fthepracticaldev.s3.amazonaws.com%2Fi%2Fx54ehgmxsfkkicfke089.png" alt="KittenRouter world map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, we are able to easily use cloudflare country codes to help us keep track of our Kitten world domination map - live!. Meow-hahaha!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Greenland, and Madagascar - I got my &lt;del&gt;plague inc&lt;/del&gt; InboxKitten eyes set on you!&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h1&gt;
  
  
  Cool! Where can I get the code
&lt;/h1&gt;

&lt;p&gt;For deployment and configuration details...&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&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%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/uilicious" rel="noopener noreferrer"&gt;
        uilicious
      &lt;/a&gt; / &lt;a href="https://github.com/uilicious/KittenRouter" rel="noopener noreferrer"&gt;
        KittenRouter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;What is KittenRouter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;KittenRouter is a routing script for &lt;a href="https://www.cloudflare.com/products/cloudflare-workers/" rel="nofollow noopener noreferrer"&gt;Cloudflare Workers&lt;/a&gt; that attempts to connect to a list of specified servers and redirect the request to whichever server that is currently 'alive' at that point of time. It is extremely useful when you have servers that may go down or are unavailable to process the request and KittenRouter can automatically attempt to redirect the request to the next configured URL for processing.&lt;/p&gt;
&lt;p&gt;At the same time, it can be configured to log down information to your ElasticSearch server for analytical purposes. Some of the information logged are the status of the servers, country of the request and etc. For the full details, see the &lt;code&gt;index.js&lt;/code&gt; file.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;How to use KittenRouter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Ultimately, KittenRouter is used together with Cloudflare workers. There are two ways in which you can use KittenRouter on your Cloudflare worker script,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using NPM modules&lt;/li&gt;
&lt;li&gt;Adding KittenRouter manually&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;1)&lt;/h3&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/uilicious/KittenRouter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://www.npmjs.com/package/kittenrouter" rel="noopener noreferrer"&gt;or alternatively NPM&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  That's cool, but Why do I even need Inboxkitten disposable email for again?
&lt;/h1&gt;

&lt;p&gt;One of the key use cases currently, and why we built this project, is to perform email validations as part of our automated test scripts. Such as the following...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Lets goto inbox kitten&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://inboxkitten.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open-Source Disposable Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Go to a random inbox inbox &lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SAMPLE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;22&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;I&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Mail Nyow!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Check that its empty&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;There for no messages for this kitten :(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Testing for regular email&lt;/span&gt;
&lt;span class="c1"&gt;// (sent using a jenkins perodic build)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://inboxkitten.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Open-Source Disposable Email&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;email&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;ik-reciever-f7s1g28&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;I&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Get Mail Nyow!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// See an email we expect, nyow&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Testing inboxkitten subject&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;With sharable test results such as &lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/7t74nVS828weKMtzGgJppF" rel="noopener noreferrer"&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%2Fvrqyi0pohzc7v1is96g0.gif" alt="uilicious demo"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Plus it's simple, cool, and fun to tinker around with.&lt;/p&gt;


&lt;h1&gt;
  
  
  What's next for KittenRouter?
&lt;/h1&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/uilicious/KittenRouter/issues/3" rel="noopener noreferrer"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg"&gt;
      &lt;span class="issue-title"&gt;
        Very rough roadmap of improvements needed for KittenRouter
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#3&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/PicoCreator" rel="noopener noreferrer"&gt;
        &lt;img class="github-liquid-tag-img" src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Favatars1.githubusercontent.com%2Fu%2F17175484%3Fv%3D4" alt="PicoCreator avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/PicoCreator" rel="noopener noreferrer"&gt;PicoCreator&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/uilicious/KittenRouter/issues/3" rel="noopener noreferrer"&gt;&lt;time&gt;May 10, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;What's next then? For KittenRouter&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;More configurable backend options
&lt;ul&gt;
&lt;li&gt;Request domain, path, region&lt;/li&gt;
&lt;li&gt;Request timeout&lt;/li&gt;
&lt;li&gt;Error / Passing HTTP code control&lt;/li&gt;
&lt;li&gt;Custom headers control&lt;/li&gt;
&lt;li&gt;Perform request as specified domain on specific IP&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;More logging options
&lt;ul&gt;
&lt;li&gt;parameters logging whitelist/blacklist for
&lt;ul&gt;
&lt;li&gt;Headers&lt;/li&gt;
&lt;li&gt;Cookies&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;More logging providers
&lt;ul&gt;
&lt;li&gt;???&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/uilicious/KittenRouter/issues/3" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Let's see where this InboxKitten journey goes next... Till then we will be taking a cat nap!&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%2Frfarhu4m4mo2d3is3nqz.jpg" 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%2Frfarhu4m4mo2d3is3nqz.jpg" alt="Cat sleeping"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Happy Shipping 🖖🏼🚀
&lt;/h1&gt;

</description>
      <category>showdev</category>
      <category>serverless</category>
      <category>cloudflare</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Javascript Array.push is 945x faster than Array.concat 🤯🤔</title>
      <dc:creator>Shi Ling</dc:creator>
      <pubDate>Thu, 02 May 2019 08:34:18 +0000</pubDate>
      <link>https://dev.to/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki</link>
      <guid>https://dev.to/uilicious/javascript-array-push-is-945x-faster-than-array-concat-1oki</guid>
      <description>&lt;h1&gt;
  
  
  TDLR
&lt;/h1&gt;

&lt;p&gt;If you are merging arrays with thousands of elements across, you can shave off seconds from the process by using &lt;code&gt;arr1.push(...arr2)&lt;/code&gt; instead of &lt;code&gt;arr1 = arr1.concat(arr2)&lt;/code&gt;. If you really to go faster, you might even want to write your own implementation to merge arrays.&lt;/p&gt;

&lt;h1&gt;
  
  
  Wait a minute... how long does it take to merge 15,000 arrays with &lt;code&gt;.concat&lt;/code&gt;...
&lt;/h1&gt;

&lt;p&gt;Recently, we had a user complaining of a major slowdown in the execution of their UI tests on &lt;a href="https://uilicious.com" rel="noopener noreferrer"&gt;UI-licious&lt;/a&gt;. Each &lt;code&gt;I.click&lt;/code&gt; &lt;code&gt;I.fill&lt;/code&gt; &lt;code&gt;I.see&lt;/code&gt; command which usually takes ~1 second to complete (post-processing e.g. taking screenshots) now took over 40 seconds to complete , so test suites that usually completed under 20 minutes took hours instead and was severely limiting their deployment process. &lt;/p&gt;

&lt;p&gt;It didn't take long for me to set up timers to narrow down out which part of the code was causing the slowdown, but I was pretty surprised when I found the culprit:&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;arr1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Array's &lt;code&gt;.concat&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;In order to allow tests to be written using simple commands like &lt;code&gt;I.click("Login")&lt;/code&gt; instead of CSS or XPATH selectors &lt;code&gt;I.click("#login-btn")&lt;/code&gt;, UI-licious works using dynamic code analysis to analyse the DOM tree to determine what and how to test your website based on semantics, accessibility attributes, and popular but non-standard patterns. The &lt;code&gt;.concat&lt;/code&gt; operations was being used to flatten the DOM tree for analysis, but worked very poorly when the DOM tree was very large and very deep, which happened when our user recently pushed an update to their application that caused their pages to bloat significantly (that's another performance issue on their side, but it's another topic).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It took 6 seconds to merge 15,000 arrays that each had an average size of 5 elements with &lt;code&gt;.concat&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;6 seconds...&lt;/p&gt;

&lt;p&gt;For 15,000 arrays with the average size of 5 elements?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's not a lot data.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Why is it so slow? Are there faster ways to merge arrays?&lt;/p&gt;




&lt;h1&gt;
  
  
  Benchmark comparisons
&lt;/h1&gt;

&lt;h2&gt;
  
  
  .push vs. .concat for 10000 arrays with 10 elements each
&lt;/h2&gt;

&lt;p&gt;So I started researching (by that, I mean googling) benchmarks for &lt;code&gt;.concat&lt;/code&gt; compared to other methods to merge arrays in Javascript. &lt;/p&gt;

&lt;p&gt;It turns out the fastest method to merge arrays is to use &lt;code&gt;.push&lt;/code&gt; which accepts n arguments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Push contents of arr2 to arr1&lt;/span&gt;
&lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;arr2&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="nx"&gt;arr2&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="p"&gt;...,&lt;/span&gt; &lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="c1"&gt;// Since my arrays are not fixed in size, I used `apply` instead&lt;/span&gt;
&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And it is faster by leaps in comparison.&lt;/p&gt;

&lt;p&gt;How fast?&lt;/p&gt;

&lt;p&gt;I ran a few performance benchmarks on my own to see for myself. Lo and behold, here's the difference on Chrome:&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%2Fnfag86cvq73ev0tvnqd9.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%2Fnfag86cvq73ev0tvnqd9.PNG" alt="JsPerf - .push vs. .concat 10000 size-10 arrays (Chrome)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://jsperf.com/javascript-array-concat-vs-push/100" rel="noopener noreferrer"&gt;Link to the test on JsPerf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To merge arrays of size 10 for 10,000 times, &lt;code&gt;.concat&lt;/code&gt; performs at 0.40 ops/sec, while &lt;code&gt;.push&lt;/code&gt; performs at 378 ops/sec. &lt;code&gt;push&lt;/code&gt; is 945x faster than &lt;code&gt;concat&lt;/code&gt;! This difference might not be linear, but it is already is already significant at this small scale. &lt;/p&gt;

&lt;p&gt;And on Firefox, here's the results:&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%2F0x80umojjrmof4fs9tfv.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%2F0x80umojjrmof4fs9tfv.PNG" alt="JsPerf - .push vs. .concat 10000 size-10 arrays (Firefox)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Firefox's SpiderMonkey Javascript engine is generally slower compared to Chrome's V8 engine, but &lt;code&gt;.push&lt;/code&gt; still comes out top, at 2260x faster.&lt;/p&gt;

&lt;p&gt;This one change to our code fixed the entire slowdown problem. &lt;/p&gt;

&lt;h2&gt;
  
  
  .push vs. .concat for 2 arrays with 50,000 elements each
&lt;/h2&gt;

&lt;p&gt;But ok, what if you are not merging 10,000 size-10 arrays, but 2 giant arrays with 50000 elements each instead? &lt;/p&gt;

&lt;p&gt;Here's the the results on Chrome along with results:&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%2F9i6w6l8l4ft0umg59jrq.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%2F9i6w6l8l4ft0umg59jrq.PNG" alt="JsPerf - .push vs. .concat 2 size-50000 arrays (chrome)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://jsperf.com/javascript-array-concat-vs-push/170" rel="noopener noreferrer"&gt;Link to the test on JsPerf&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.push&lt;/code&gt; is still faster than &lt;code&gt;.concat&lt;/code&gt;, but a factor of 9.&lt;/p&gt;

&lt;p&gt;Not as dramatic as 945x slower, but still dang slow.&lt;/p&gt;




&lt;h2&gt;
  
  
  Prettier syntax with rest spread
&lt;/h2&gt;

&lt;p&gt;If you find &lt;code&gt;Array.prototype.push.apply(arr1, arr2)&lt;/code&gt; verbose, you can use a simple variant using the rest spread ES6 syntax:&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;arr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The performance difference between &lt;code&gt;Array.prototype.push.apply(arr1, arr2)&lt;/code&gt; and &lt;code&gt;arr1.push(...arr2)&lt;/code&gt; is negligable.&lt;/p&gt;




&lt;h1&gt;
  
  
  But why is &lt;code&gt;Array.concat&lt;/code&gt; so slow?
&lt;/h1&gt;

&lt;p&gt;It lot of it has to do with the Javascript engine, but I don't know the exact answer, so I asked my buddy &lt;a class="mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;, the co-creator of &lt;a href="http://gpu.rocks/" rel="noopener noreferrer"&gt;GPU.js&lt;/a&gt;, as he had spent a fair bit of time digging around the V8 source code before. &lt;a class="mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;'s also lent me his sweet gaming PC which he used to benchmark GPU.js to run the JsPerf tests because my MacBook didn't have the memory to even perform &lt;code&gt;.concat&lt;/code&gt; with two size-50000 arrays.&lt;/p&gt;

&lt;p&gt;Apparently the answer has a lot to do with the fact that &lt;code&gt;.concat&lt;/code&gt; creates a new array while &lt;code&gt;.push&lt;/code&gt; modifies the first array. The additional work &lt;code&gt;.concat&lt;/code&gt; does to add the elements from the first array to the returned array is the main reason for the slowdown.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Me: "What? Really? That's it? But by that much? No way!"&lt;br&gt;
&lt;a class="mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;: "Serious, just try writing some naive implementations of .concat vs .push then!"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So I tried writing some naive implementations of &lt;code&gt;.concat&lt;/code&gt; and &lt;code&gt;.push&lt;/code&gt;. Several in fact, plus a comparison with &lt;a href="https://lodash.com/" rel="noopener noreferrer"&gt;lodash&lt;/a&gt;'s &lt;code&gt;_.concat&lt;/code&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%2Fvoipcak9p1rzsk01pfou.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%2Fvoipcak9p1rzsk01pfou.PNG" alt="JsPerf - Various ways to merge arrays (Chrome)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://jsperf.com/merge-array-implementations/1" rel="noopener noreferrer"&gt;Link to the test on JsPerf&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Naive implementation 1
&lt;/h2&gt;

&lt;p&gt;Let's talk about the first set of naive implementation:&lt;/p&gt;

&lt;h5&gt;
  
  
  Naive implementation of &lt;code&gt;.concat&lt;/code&gt;
&lt;/h5&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Create result array&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;arr3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

&lt;span class="c1"&gt;// Add Array 1&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr1Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Add Array 2&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;arr1Length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;h5&gt;
  
  
  Naive implementation of &lt;code&gt;.push&lt;/code&gt;
&lt;/h5&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;arr1Length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As you can see, the only difference between the two is that the &lt;code&gt;.push&lt;/code&gt; implementation modifies the first array directly.&lt;/p&gt;

&lt;h5&gt;
  
  
  Results of vanilla methods:
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.concat&lt;/code&gt; : 75 ops/sec&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.push&lt;/code&gt;: 793 ops/sec (10x faster)&lt;/li&gt;
&lt;/ul&gt;

&lt;h5&gt;
  
  
  Results of naive implementation 1
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.concat&lt;/code&gt; : 536 ops/sec&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.push&lt;/code&gt; : 11,104 ops/sec (20x faster)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It turns that my DIY &lt;code&gt;concat&lt;/code&gt; and &lt;code&gt;push&lt;/code&gt; is faster than the vanilla implementations... But here we can see that simply creating a new result array and copying the content of the first array over slows down the process significantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Naive implementation 2 (Preallocate size of the final array)
&lt;/h2&gt;

&lt;p&gt;We can further improve the naive implementations by preallocating the size of the array before adding the elements, and this makes a huge difference.&lt;/p&gt;

&lt;h5&gt;
  
  
  Naive implementation of &lt;code&gt;.concat&lt;/code&gt; with pre-allocation
&lt;/h5&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Create result array with preallocated size&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;arr3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr1Length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Add Array 1&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr1Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Add Array 2&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr3&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;arr1Length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;h5&gt;
  
  
  Naive implementation of &lt;code&gt;.push&lt;/code&gt; with pre-allocation
&lt;/h5&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Pre allocate size&lt;/span&gt;
&lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr1Length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;

&lt;span class="c1"&gt;// Add arr2 items to arr1&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;arr1Length&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;h5&gt;
  
  
  Results of naive implementation 1
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.concat&lt;/code&gt; : 536 ops/sec&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.push&lt;/code&gt; : 11,104 ops/sec (20x faster)&lt;/li&gt;
&lt;/ul&gt;
&lt;h5&gt;
  
  
  Results of naive implementation 2
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.concat&lt;/code&gt; : 1,578 ops/sec&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.push&lt;/code&gt; : 18,996 ops/sec (12x faster)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Preallocating the size of the final array improves the performance by 2-3 times for each method.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;.push&lt;/code&gt; array vs. &lt;code&gt;.push&lt;/code&gt; elements individually
&lt;/h2&gt;

&lt;p&gt;Ok, what if we just .push elements individually? Is that faster than &lt;code&gt;Array.prototype.push.apply(arr1, arr2)&lt;/code&gt;&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;arr2Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
  &lt;span class="nx"&gt;arr1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&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;h5&gt;
  
  
  Results
&lt;/h5&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.push&lt;/code&gt; entire array: 793 ops/sec&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.push&lt;/code&gt; elements individually: 735 ops/sec (slower)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So doing &lt;code&gt;.push&lt;/code&gt; on individual elements is slower than doing &lt;code&gt;.push&lt;/code&gt; on the entire array. Makes sense.&lt;/p&gt;
&lt;h1&gt;
  
  
  Conclusion: Why &lt;code&gt;.push&lt;/code&gt; is faster &lt;code&gt;.concat&lt;/code&gt;
&lt;/h1&gt;

&lt;p&gt;In conclusion, it is true that the main reason why &lt;code&gt;concat&lt;/code&gt; is so much slower than &lt;code&gt;.push&lt;/code&gt; is simply that it creates a new array and does the additional work to copy the first array over.&lt;/p&gt;

&lt;p&gt;That said, now there's another mystery to me... &lt;/p&gt;
&lt;h1&gt;
  
  
  Another mystery
&lt;/h1&gt;

&lt;p&gt;Why are the vanilla implementations so much slower than the naive implementations?🤔I asked for &lt;a class="mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;'s help again. &lt;/p&gt;

&lt;p&gt;We took a look at lodash's &lt;code&gt;_.concat&lt;/code&gt; implementation for some hints as to what else is vanilla &lt;code&gt;.concat&lt;/code&gt; doing under the hood, as it is comparable in performance (lodash's is slightly faster).&lt;/p&gt;

&lt;p&gt;It turns out that because according to the vanilla's &lt;code&gt;.concat&lt;/code&gt;'s specs, the method is overloaded, and supports two signatures: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Values to append as n number of arguments, e.g. &lt;code&gt;[1,2].concat(3,4,5)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The array to append itself, e.g. &lt;code&gt;[1,2].concat([3,4,5])&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can even do both like this: &lt;code&gt;[1,2].concat(3,4,[5,6])&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Lodash also handles both overloaded signatures, and to do so, lodash puts all the arguments into an array, and flattens it. It make sense if you are passing in several arrays as arguments. But when passed an array to append, it doesn't just use the array as it is, it copies that into another array, and then flattens it. &lt;/p&gt;

&lt;p&gt;... ok...&lt;/p&gt;

&lt;p&gt;Definitely could be more optimised. And this is why you might want to DIY your own merge array implementation.&lt;/p&gt;

&lt;p&gt;Also, it's just my and &lt;a class="mentioned-user" href="https://dev.to/picocreator"&gt;@picocreator&lt;/a&gt;'s theory of how vanilla &lt;code&gt;.concat&lt;/code&gt; works under the hood based on Lodash's source code and his slightly outdated knowledge of the V8 source code.&lt;/p&gt;

&lt;p&gt;You can read the lodash's source code at your leisure &lt;a href="https://github.com/lodash/lodash/blob/4.17.11/lodash.js#L6913" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Additional Notes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The tests are done with Arrays that only contain Integers. Javascript engines are known to perform faster with Typed Arrays. The results are expected to be slower if you have objects in the arrays.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Here are the specs for the PC used to run the benchmarks:&lt;br&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%2Fe86nvx39b26n4k1gukqd.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%2Fe86nvx39b26n4k1gukqd.PNG" alt="PC specs for the performance tests"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;


&lt;h1&gt;
  
  
  Why are we doing such large array operations during UI-licious tests anyway?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE" rel="noopener noreferrer"&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%2Fnreaiqlqnokx20xn8zym.gif" alt="Uilicious Snippet dev.to test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Under the hood, the UI-licious test engine scans the DOM tree of the target application, evaluating the semantics, accessible attributes and other common patterns to determine what is the target element and how to test it. &lt;/p&gt;

&lt;p&gt;This is so that we can make sure tests can be written as simple as this:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;

&lt;span class="c1"&gt;// Lets go to dev.to&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Fill up search&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search&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;uilicious&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// I should see myself or my co-founder&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shi Ling&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eugene Cheah&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;Without the use of CSS or XPATH selectors, so that the tests can be more readable, less sensitive to changes in the UI, and easier to maintain.&lt;/p&gt;

&lt;h3&gt;
  
  
  ATTENTION: Public service announcement - Please keep your DOM count low!
&lt;/h3&gt;

&lt;p&gt;Unfortunately, there's a trend of DOM trees growing excessively large these days because people are building more and more complex and dynamic applications with modern front-end frameworks. It's a double-edge sword, frameworks allow us to develop faster, folks often forget how much bloat frameworks add. I sometimes cringe at the number of elements that are just there to wrap other elements when inspecting the source code of various websites. &lt;/p&gt;

&lt;p&gt;If you want to find out whether your website has too many DOM nodes, you can run a &lt;a href="https://developers.google.com/web/tools/lighthouse/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; audit. &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%2Fdevelopers.google.com%2Fweb%2Fprogressive-web-apps%2Fimages%2Fpwa-lighthouse.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%2Fdevelopers.google.com%2Fweb%2Fprogressive-web-apps%2Fimages%2Fpwa-lighthouse.png" alt="Google Lighthouse"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to Google, the optimal DOM tree is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less than 1500 nodes&lt;/li&gt;
&lt;li&gt;Depth size of less than 32 levels&lt;/li&gt;
&lt;li&gt;A parent node has less than 60 children&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A quick audit on the Dev.to feed shows that the DOM tree size is pretty good:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Total count of 941 nodes&lt;/li&gt;
&lt;li&gt;Max. depth of 14&lt;/li&gt;
&lt;li&gt;Max number of child elements at 49&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not bad!&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>javascript</category>
      <category>webperf</category>
    </item>
    <item>
      <title>🤔 UI framework wars &amp; testing survey (of Startups &amp; SME's in 🇸🇬 Singapore)</title>
      <dc:creator>Eugene Cheah</dc:creator>
      <pubDate>Tue, 23 Apr 2019 08:30:34 +0000</pubDate>
      <link>https://dev.to/uilicious/ui-framework-wars-testing-survey-results-on-startups-sme-s-in-singapore-1bj1</link>
      <guid>https://dev.to/uilicious/ui-framework-wars-testing-survey-results-on-startups-sme-s-in-singapore-1bj1</guid>
      <description>&lt;p&gt;Hey there,&lt;/p&gt;

&lt;p&gt;For the past month, the team of Uilicious has been going around one north (our country startup hub), and a few other business hubs, knocking over 200 doors to survey the folks and find out what's the most popular front end frameworks used in production and the state of testing in Singapore 🇸🇬&lt;/p&gt;

&lt;p&gt;Something that Uilicious as a company did on a much smaller scale 2 years ago, to get the general sense of things. And hopefully, plan to repeat for the next few years.&lt;/p&gt;

&lt;p&gt;🤔 Sounds good? let's get to the results&lt;/p&gt;




&lt;h1&gt;
  
  
  UI Framework Wars - in production?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;the endgame answer everyone wants to know on the ongoing UI "standard" war&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HDOUXdke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjppaejqiqho3oexe47l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HDOUXdke--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/pjppaejqiqho3oexe47l.png" alt="XKCD 927 - standards"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vS2on42s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xlpqswtgrpw17etfubzk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vS2on42s--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xlpqswtgrpw17etfubzk.png" alt="UI Framework Wars"&gt;&lt;/a&gt;&lt;/p&gt;
Note that the total is more than 100%, as users are allowed to select multiple options



&lt;p&gt;React was hands down the winner 🔥 when it comes to applications developed in production.&lt;/p&gt;

&lt;p&gt;The emphasis on production is intentional. After all, with the recent changes and hype of newer frameworks it is important to remember that it is slow and tedious for production workload under daily usage and development to constantly stay on the hype train and takes a long time to migrate from one framework to another. With JQuery being a close 2nd place on the list or higher for easily the past 10 years 😴&lt;/p&gt;

&lt;p&gt;Another surprising contender, Ruby on Rails. Originally not an option among the list, it had such a significant amount of responders in the "Others" free text option, that it was added onto the list.&lt;/p&gt;

&lt;p&gt;Sad contenders for me personally, is Vue.js is not as popular as I hoped (disclosure, we are Vue.js supporters and help organize their meetups locally). &lt;/p&gt;

&lt;p&gt;Another perhaps more surprising result: 0 response for polymer web components, which was an option in the survey. Probably because it's a little too bleeding edge for production 🚅&lt;/p&gt;




&lt;h1&gt;
  
  
  Development Team Size?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mv_c_dEu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c8uvzwkvj15aj9udogyp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mv_c_dEu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/c8uvzwkvj15aj9udogyp.png" alt="Development Team Size"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of the teams surveyed, a good half has a development team size between 5-10. This is a good indicator that most companies have outgrown their initial startup days. &lt;/p&gt;

&lt;h1&gt;
  
  
  Who does testing?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V024aTDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jmeb3bbit290zpaibat9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V024aTDv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/jmeb3bbit290zpaibat9.png" alt="Who does testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Interestingly, despite the labor crunch of programmers, most companies heavily rely (48.5%) on their developers for testing. &lt;/p&gt;

&lt;p&gt;This is worrying in two ways. One is that developers are typically not a good testers of their own code due to tunnel vision. Two, they are also the most costly option, with the average developer salary being easily double a dedicated manual QA salary 😶&lt;/p&gt;

&lt;p&gt;However what was even more shocking 😱 was that a significant 15% of respondent indicated that they do not test, and this included large dev teams that are &amp;gt;10. &lt;/p&gt;

&lt;p&gt;Followed by another 15%, who use their customer themselves, or their business stakeholders for testing.&lt;/p&gt;

&lt;p&gt;Although some indicated that this is not a problem, especially those who serve as a solution vendor, this will lead to an unhealthy adversarial relationship between the development team and the business team. &lt;br&gt;
Tension worsen when previously fixed bugs reappear through regression testing and the business team who in most cases, faces the wrath of the clients and the impact of any client business lost.&lt;/p&gt;

&lt;p&gt;Something that ideally a good QA team or even a single dedicated QA tester can help manage, which thankfully 20% of the respondents had. So a small win here 👍 &lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;Side note: The results below, excludes teams without a testing process.&lt;/p&gt;

&lt;p&gt;Unkown represent respondents who are not directly involved in the full testing process, and hence could not give an accurate answer &lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  How much time does your team spend on testing?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WCCKZlSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6f6ypjjo3ff0fj9fheki.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WCCKZlSj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/6f6ypjjo3ff0fj9fheki.png" alt="Team testing time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While not shown in the chart, having a dedicated QA helps bring the overall development team time spent on testing downwards, with most in the 0-10%, and 10-25% category 😃&lt;/p&gt;

&lt;p&gt;Insufficient test coverage tends to be the main issue for teams who spend more than 25% of their time testing. Along with the practical time-consuming nature of manually performing testing on various devices and screen sizes ⌛&lt;/p&gt;

&lt;p&gt;This is further compounded if the developers are the testers, as it is not an effective use of their time.&lt;/p&gt;
&lt;h1&gt;
  
  
  How often do you test your application?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tk06-DJf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u5gaiwzcs9980mlz7euu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tk06-DJf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/u5gaiwzcs9980mlz7euu.png" alt="Testing frequency"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A large 40% of teams seem to be running on a weekly or bi-weekly release cycle of development and testing.&lt;/p&gt;

&lt;p&gt;Followed by as and when deployments are done on an (unknown schedule), which is typically once a month or less. &lt;/p&gt;

&lt;p&gt;Finally a round of applause for the rare 3% who either automate their test to run every single workday or on every commit 😎&lt;/p&gt;
&lt;h1&gt;
  
  
  What tools do you use to test?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mL4W6f9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xjg2cd7n6arj6lo5s2h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mL4W6f9V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/xjg2cd7n6arj6lo5s2h5.png" alt="Testing tools"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Despite the whole growth of high tech web apps using the next buzz from AI to the blockchain. Testing is happening in rather low-tech ways, manually for all these apps 🤐&lt;/p&gt;

&lt;p&gt;One thing heartening to see is the rise of test automation at 27%, this is significantly higher then the ~5% result we saw two years ago. Perhaps indicative of a slowly maturing industry.&lt;/p&gt;


&lt;h1&gt;
  
  
  How often do you get a bug report from customers/business team?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hpI-tCeW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7sn29pu2snehk45lxyoo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hpI-tCeW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7sn29pu2snehk45lxyoo.png" alt="Bug Frequency"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A particularly painful question for most. From what it seems, daily and weekly bug reports seem to be the daily grind faced by 75% of the teams.&lt;/p&gt;

&lt;p&gt;Something which almost every dev dread to get from a customer 😭&lt;/p&gt;
&lt;h1&gt;
  
  
  What is the biggest challenge for testing?
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VVA95-gA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ktb1kbhcs453ijipiu0t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VVA95-gA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/ktb1kbhcs453ijipiu0t.png" alt="Challenges for testing"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, there is the open-ended question on what is the biggest challenge teams faced for testing.&lt;/p&gt;

&lt;p&gt;Unsurprisingly, communication is still the number one challenge at 38%. A great shout out to &lt;a href="https://en.wikipedia.org/wiki/The_Mythical_Man-Month"&gt;The Mythical Man-Month&lt;/a&gt; for those who read it.&lt;/p&gt;

&lt;p&gt;Miscommunication of requirements ranges anywhere between any of the following: Customers, Business Team, Developers, Designers, Managers and Testers.&lt;/p&gt;

&lt;p&gt;This is followed by the lack of manpower in the current talent crunch (27%) and the scale of web testing various screen sizes, pages, and browsers (18%). All of which is something Uilicious as a company will be glad to help with.&lt;/p&gt;

&lt;p&gt;And finally the startup classic, no budget. At 12%, this never changes 😫&lt;/p&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Q_MLC8i_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9gxtdlkcf7zb7r7ft0pm.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Q_MLC8i_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9gxtdlkcf7zb7r7ft0pm.jpg" alt="Hmm...."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The following are personal observations and opinions regarding the survey. AKA. it's not backed by hard data.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h1&gt;
  
  
  Personal observations on testing
&lt;/h1&gt;

&lt;p&gt;What we found interesting was that most companies surveyed either have their entire development team or the majority of the team outsourced overseas, an indicator of the ongoing programmer manpower crunch in Singapore 🤔&lt;/p&gt;

&lt;p&gt;A worrying observation, from the various interview, was that most teams who test manually, only tend to do so for new features. Without any regression check. Or more scarily, those that do no testing.&lt;/p&gt;

&lt;p&gt;This is understandable for extremely early-stage startups, who just started. But, several were not in this stage, including larger "Series-A/B" level startups.&lt;/p&gt;

&lt;p&gt;This is worrying personally as a consumer of the modern web, as this would explain many times what has happened when extremely obvious bugs appear when using an application (such as login and checkout), where simple regression testing is missing.&lt;/p&gt;

&lt;p&gt;It also raises questions in me, if a company is not willing to invest any resources into testing functionality, what resources are in place to ensure the security of their system? Especially considering my own personal data on some of these applications.&lt;/p&gt;

&lt;p&gt;Something I hope will be improved on in time 😧&lt;/p&gt;
&lt;h1&gt;
  
  
  Personal observations on UI framework
&lt;/h1&gt;

&lt;p&gt;On the UI framework war side of things, many responders during the interview process mentioned plans to experiment and move away from React onto other frameworks for new projects or as part of their migration plan. So perhaps next year React will slowly topple down?&lt;/p&gt;
bias mode engaged



&lt;p&gt;&lt;strong&gt;Go Vue, go 🏎️&lt;/strong&gt;&lt;/p&gt;



&lt;blockquote&gt;
&lt;p&gt;So how can Uilicious help make this better?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Uilicious.com testing platform
&lt;/h1&gt;

&lt;p&gt;First, we are a test automation platform aiming to remove all the pain points in website testing (hence the survey). And we are constantly iterating based on feedback.&lt;/p&gt;

&lt;p&gt;To get users up and running quickly, our platform allows testers to easily write test scripts like these ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Lets go to dev.to&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://dev.to&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Fill up search&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Search&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;uilicious&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pressEnter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// I should see myself or my co-founder&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shi Ling&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;I&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;see&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Eugene Cheah&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;And churn out &lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE"&gt;sharable test result&lt;/a&gt; like these ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://snippet.uilicious.com/test/public/1cUHCW368zsHrByzHCkzLE"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5llcnkKt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/gyrtj5lk2b2bn89z7ra1.gif" alt="Uilicious Snippet dev.to test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Uilicious QA-as-a-service
&lt;/h1&gt;

&lt;p&gt;Second, we have since then soft launched &lt;a href="https://services.uilicious.com/qa-service"&gt;Uilicious Services&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;As made obvious in the survey result. Most teams lack the resources required to form their own dedicated Quality Assurance team. And that is something we can help with, from gathering testing requirements to the writing and maintaining of tests.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://services.uilicious.com/qa-service"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yAlFPb4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/zdxp8woa1ptynq7c57yq.png" alt="Uilicious Services"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the above testing challenges in the survey apply to you, let us know and we will be glad to help with our Uilicious testing service, to get your full QA process up and running with our experienced QA engineers. &lt;/p&gt;

&lt;p&gt;Also once the team grows large enough to have their own dedicated QA team, they can take over the role of writing and maintaining test scripts on the platform.&lt;/p&gt;

&lt;p&gt;We hope that through the testing service, we can help teams to convince their project managers on to improve the overall quality of their website and product 😉 and get dev teams up and running faster, with their projects, alongside our test platform. &lt;/p&gt;



bias mode dis-engaged



&lt;h1&gt;
  
  
  What should we improve on for next year survey?
&lt;/h1&gt;

&lt;p&gt;One notable thing: is that due to the current survey size (~200), and the nature of how the data is collected (almost entirely at one-north). There is a heavy bias towards smaller startups. So take the result as vague indicators at best. Something we hope to improve on in the future 😵&lt;/p&gt;

&lt;p&gt;Another thing to note - due to the nature of how this survey is conducted, the result greatly underrepresents the larger development teams.&lt;/p&gt;

&lt;p&gt;Many of the much larger startups (who probably have a larger development team) would have an office manager at the entrance, turning away any survey requests 😥&lt;/p&gt;

&lt;p&gt;When we repeat the survey next year, we will ask for both the local and overseas team size separately, which would give some insights on the rate of outsourcing in Singapore for developers.&lt;/p&gt;

&lt;p&gt;Similarly, there are clear obvious differences in testing requirements for solutions vendor, or a web company. Something we would try to add next year. Along with the question of "how old the company is".&lt;/p&gt;

&lt;p&gt;So if you have any ideas on how we should improve the survey, do let me know.&lt;/p&gt;

&lt;p&gt;Till then, as a startup founder myself. I gotta get back to investors and fundraising to grow Uilicious, so gotta go now...&lt;/p&gt;




&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zqnDCo3t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/46wdha7noxft6xch4owu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zqnDCo3t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/46wdha7noxft6xch4owu.png" alt="Gotta Go"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Happy shipping 🖖🏼🚀
&lt;/h1&gt;

</description>
      <category>showdev</category>
      <category>webdev</category>
      <category>testing</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
