<?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>Forem: Gemma Black</title>
    <description>The latest articles on Forem by Gemma Black (@gemmablack).</description>
    <link>https://forem.com/gemmablack</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F54882%2F47ccbd05-be44-4a73-bb16-acd5805b5a13.jpg</url>
      <title>Forem: Gemma Black</title>
      <link>https://forem.com/gemmablack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/gemmablack"/>
    <language>en</language>
    <item>
      <title>Understanding Sequelize's 'escape' function and why ideally we should avoid using it manually</title>
      <dc:creator>Gemma Black</dc:creator>
      <pubDate>Sat, 23 Nov 2024 10:32:56 +0000</pubDate>
      <link>https://forem.com/gemmablack/understanding-sequelizes-escape-function-and-why-ideally-we-should-avoid-using-it-19em</link>
      <guid>https://forem.com/gemmablack/understanding-sequelizes-escape-function-and-why-ideally-we-should-avoid-using-it-19em</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;So a disclaimer. As a software engineer, I'm not a security expert. I rely heavily on those who know better, especially OWASP. So all feedback is welcome to help fix any flaws in the article or improve it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sequelize is a very robust database ORM for Node.js. Knowing how to use it is one thing. Knowing how it works under the hood is another.&lt;/p&gt;

&lt;p&gt;So when I was asked recently, why I had not used the escape function in &lt;a href="https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#applying-where-clauses" rel="noopener noreferrer"&gt;Sequelize's where&lt;/a&gt; clause, I said, properties in the where clause are already escaped. But was I correct? Or was that an assumption? Even more, was I following the best practices for preventing SQL injections.&lt;/p&gt;

&lt;p&gt;To answer, first, let's explore how SQL injection attacks happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preparing our database
&lt;/h2&gt;

&lt;p&gt;We want to prepare a database to test how a SQL injection could happen.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You can use any database you like to do this but it'll be easier if you use SQLite or Postgres as they have similar escaping syntaxes whereas MySQL is a law unto itself 😁.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a new SQLite database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sqlite3 database.sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a basic users table.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Insert some users into the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'John Doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'john@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Bruch Wayne'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bruce@example.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select those users from the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you should see two users.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;John Doe&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:john@example.com"&gt;john@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bruch Wayne&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:bruce@example.com"&gt;bruce@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Selecting a single user
&lt;/h2&gt;

&lt;p&gt;If you have a multi-tenant application, where users can only see their data, you'll usually do lookups by a single user ID. That way, you don't expose information to the wrong person.&lt;/p&gt;

&lt;p&gt;Query a user by their id.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;id&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we only see one user.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;John Doe&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:john@example.com"&gt;john@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Attempting a SQL injection attack
&lt;/h2&gt;

&lt;p&gt;But what if we pretend to we have a SQL injection attack? Add &lt;code&gt;or '1' = '1'&lt;/code&gt; to the end of the SQL query, and now that the last statement is always true, all users are returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="k"&gt;or&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;name&lt;/th&gt;
&lt;th&gt;email&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;John Doe&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:john@example.com"&gt;john@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Bruch Wayne&lt;/td&gt;
&lt;td&gt;&lt;a href="mailto:bruce@example.com"&gt;bruce@example.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Users shouldn't have direct access to our database, so how would this disastrous situation happen in reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a Node.js app as an example
&lt;/h2&gt;

&lt;p&gt;Initalise npm in a new folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install Sequelize and its dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;sequelize sequelize-cli sqlite3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a basic index.js file which connects to the sqlite database (or your flavour of 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;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Sequelize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DataTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sequelize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sequelize&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;Sequelize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./database.sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// test the connection is Ok&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Get the argument from the input&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`SELECT * FROM Users WHERE id = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Raw query results:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to connect to the database:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;ul&gt;
&lt;li&gt;It works by taking an argument from the input in the terminal.&lt;/li&gt;
&lt;li&gt;It runs the query.&lt;/li&gt;
&lt;li&gt;It outputs the results.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Running a safe query against our script
&lt;/h2&gt;

&lt;p&gt;Running the query produces the following output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT 1+1 AS result
Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT &lt;span class="k"&gt;*&lt;/span&gt; FROM Users WHERE &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1

Raw query results: &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;: 1, name: &lt;span class="s1"&gt;'John Doe'&lt;/span&gt;, email: &lt;span class="s1"&gt;'john@example.com'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That successfully returned the user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a SQL injection attack against our script
&lt;/h2&gt;

&lt;p&gt;Repeating the SQL injection attack we tried before, the outcome shows the disaster. We can access all users when we shouldn't be able to!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js &lt;span class="s2"&gt;"1 or '1' = '1'"&lt;/span&gt; 

Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT 1+1 AS result
Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT &lt;span class="k"&gt;*&lt;/span&gt; FROM Users WHERE &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1 or &lt;span class="s1"&gt;'1'&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1'&lt;/span&gt;

Raw query results: &lt;span class="o"&gt;[&lt;/span&gt;
  &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;: 1, name: &lt;span class="s1"&gt;'John Doe'&lt;/span&gt;, email: &lt;span class="s1"&gt;'john@example.com'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;,
  &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;: 2, name: &lt;span class="s1"&gt;'Bruch Wayne'&lt;/span&gt;, email: &lt;span class="s1"&gt;'bruce@example.com'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So how can we fix this?&lt;/p&gt;

&lt;h2&gt;
  
  
  First attempt trying to escape user input
&lt;/h2&gt;

&lt;p&gt;Sequelize wraps the query in quotes. So we'll do just that. Replace the &lt;code&gt;const id&lt;/code&gt; line with this.&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&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 voila. That's fixed the injection...partly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node 02_index.js &lt;span class="s2"&gt;"1 or 1=1"&lt;/span&gt;

Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT 1+1 AS result
Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT &lt;span class="k"&gt;*&lt;/span&gt; FROM Users WHERE &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1 or 1=1'&lt;/span&gt;

Raw query results: &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Selecting users by id with &lt;code&gt;1 or 1=1&lt;/code&gt; doesn't match anything. So nothing is returned.&lt;/p&gt;

&lt;p&gt;But what if we were to try and inject some apostrophes to end the first statement and add our own &lt;code&gt;OR&lt;/code&gt; statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node 02_index.js "1' or 1=1 or '"

Executing (default): SELECT 1+1 AS result
Executing (default): SELECT * FROM Users WHERE id = '1' or 1=1 or ''

Raw query results: [
  { id: 1, name: 'John Doe', email: 'john@example.com' },
  { id: 2, name: 'Bruch Wayne', email: 'bruce@example.com' }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the disaster returns! We can get all users again!&lt;/p&gt;

&lt;h2&gt;
  
  
  Escaping user inputted quotes
&lt;/h2&gt;

&lt;p&gt;We need to escape the quotes as well, just like Sequelize does.&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/'/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;''&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node 03_index.js &lt;span class="s2"&gt;"1' or 1=1 or '"&lt;/span&gt;

Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT 1+1 AS result
Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT &lt;span class="k"&gt;*&lt;/span&gt; FROM Users WHERE &lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1'' or 1=1 or '''&lt;/span&gt;

Raw query results: &lt;span class="o"&gt;[]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see how the whole paramater is a string in the last query.&lt;/p&gt;

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

&lt;p&gt;And this is what the Sequelize escape function does but is far more comprehensive, covering different sql dialects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sequelize escape function
&lt;/h2&gt;

&lt;p&gt;Instead of doing our own escaping, it's best to leave it to libraries that are battle-tested and proven.&lt;/p&gt;

&lt;p&gt;It is safer just to do this.&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;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Escaping is not the best option against SQL injection attacks
&lt;/h2&gt;

&lt;p&gt;OWASP puts escaping user input as last on its list of acceptable ways of preventing sql injections. It's strongly discouraged!&lt;/p&gt;

&lt;p&gt;Why?&lt;/p&gt;

&lt;p&gt;Potentially the responsibility is on the developer to remember to escape each and every single user input. Even using Sequelize here, if the developer forgot, then this would be a security breach waiting to happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's better than escaping
&lt;/h2&gt;

&lt;p&gt;Using Sequelize's query builder is better than escaping manually.&lt;/p&gt;

&lt;p&gt;As an example, create a user model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx sequelize-cli model:generate &lt;span class="nt"&gt;--name&lt;/span&gt; User &lt;span class="nt"&gt;--attributes&lt;/span&gt; name:string,email:string
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the user model to the main script and replace the raw query with the model-based one.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Sequelize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;DataTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sequelize&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sequelize&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;Sequelize&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;dialect&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sqlite&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./database.sqlite&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./models&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
            &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&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="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;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Raw query results:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&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;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unable to connect to the database:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;When we run our SQL injection attack again, we now get undefined. No need to use Sequelize escape, the where clause does it for us.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node 05_index.js &lt;span class="s2"&gt;"1' or 1=1 or '"&lt;/span&gt;

Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT 1+1 AS result
Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;, &lt;span class="sb"&gt;`&lt;/span&gt;name&lt;span class="sb"&gt;`&lt;/span&gt;, &lt;span class="sb"&gt;`&lt;/span&gt;email&lt;span class="sb"&gt;`&lt;/span&gt; FROM &lt;span class="sb"&gt;`&lt;/span&gt;Users&lt;span class="sb"&gt;`&lt;/span&gt; AS &lt;span class="sb"&gt;`&lt;/span&gt;User&lt;span class="sb"&gt;`&lt;/span&gt; WHERE &lt;span class="sb"&gt;`&lt;/span&gt;User&lt;span class="sb"&gt;`&lt;/span&gt;.&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1'' or 1=1 or '''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

Raw query results: undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what about if we use Op.like in a where statement?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We would never do this on an ID but the principle is the same.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
            &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;id&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;Sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;like&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final query shows the entire statement is escaped.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Bind parameter. Better than escaping.
&lt;/h2&gt;

&lt;p&gt;Parameterised queries are great because we send the sql query separately from the data.&lt;/p&gt;

&lt;p&gt;Note that was use a bind parameter, &lt;code&gt;'%:id%'&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;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; 
            &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="na"&gt;id&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;Sequelize&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Op&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;like&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;%:id%&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="na"&gt;bind&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&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;Notice what is sent to the database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Executing &lt;span class="o"&gt;(&lt;/span&gt;default&lt;span class="o"&gt;)&lt;/span&gt;: SELECT &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;, &lt;span class="sb"&gt;`&lt;/span&gt;name&lt;span class="sb"&gt;`&lt;/span&gt;, &lt;span class="sb"&gt;`&lt;/span&gt;email&lt;span class="sb"&gt;`&lt;/span&gt; FROM &lt;span class="sb"&gt;`&lt;/span&gt;Users&lt;span class="sb"&gt;`&lt;/span&gt; AS &lt;span class="sb"&gt;`&lt;/span&gt;User&lt;span class="sb"&gt;`&lt;/span&gt; WHERE &lt;span class="sb"&gt;`&lt;/span&gt;User&lt;span class="sb"&gt;`&lt;/span&gt;.&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; LIKE &lt;span class="s1"&gt;'%:id%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

Raw query results: undefined
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The database engine knows exactly what to do with the data, and how to escape it. If we send the query syntax plus the data all as one, then we have to escape it before it gets to the database.&lt;/p&gt;

&lt;p&gt;Parameterised queries are the &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;recommended way&lt;/a&gt; (according to OWASP) of making queries of any kind with user input, in the database.&lt;/p&gt;

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

&lt;p&gt;It seems using the where statement without bind parameters doesn't follow best practices from OWASP. So that's something I need to improve there.&lt;/p&gt;

&lt;p&gt;And thank you for reading. If I've made any mistakes, do let me know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading material
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05-Testing_for_SQL_Injection" rel="noopener noreferrer"&gt;https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05-Testing_for_SQL_Injection&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sequelize</category>
      <category>node</category>
      <category>sql</category>
      <category>owasp</category>
    </item>
    <item>
      <title>16 Principles for Tech-led Start-ups as a Software Engineer</title>
      <dc:creator>Gemma Black</dc:creator>
      <pubDate>Mon, 29 Jul 2024 11:46:26 +0000</pubDate>
      <link>https://forem.com/gemmablack/16-principles-for-tech-led-start-ups-as-software-engineer-5gjk</link>
      <guid>https://forem.com/gemmablack/16-principles-for-tech-led-start-ups-as-software-engineer-5gjk</guid>
      <description>&lt;p&gt;After a few years at a start-up turned scale-up, learning a few hard lessons along the way, I decided to share some principles I think are universal. I’m sure there'll be exceptions, like start-ups with near-unlimited runways funded by a generous benefactor, but for the rest of us, we have only so much time before we have to call it quits.&lt;/p&gt;

&lt;p&gt;Two goals will influence all the principles I share. &lt;/p&gt;

&lt;p&gt;1) Achieving speed.&lt;br&gt;
2) Propelling growth. &lt;/p&gt;

&lt;p&gt;I’ll start with general principles, and then more technical ones as I go on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus
&lt;/h2&gt;

&lt;p&gt;Even if we're a one-person startup, our singular goal can get lost. It's even more difficult to keep focused with a multi-person team. But we have to pull in one direction.&lt;/p&gt;

&lt;p&gt;We can't make progress by going around in circles. So there needs to be regular reminders of what the focus is for the month, week, day and even hour. It's too easy to forget our goal and get side-tracked.&lt;/p&gt;

&lt;p&gt;And what things do we need to focus on? It depends on where we are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Getting the first customer. E.g. Do we need to get that first feature out the door? How quickly can we iterate?&lt;/li&gt;
&lt;li&gt;Growing to &lt;em&gt;N&lt;/em&gt; customers. E.g. Do we need more features? How quickly can we release more?&lt;/li&gt;
&lt;li&gt;Preventing churn. E.g. Do we need to fix bugs and improve stability?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Essentialism
&lt;/h2&gt;

&lt;p&gt;"Essentialism isn't about getting more done in less time. It's about getting only the right things done" - says a review for the book, Essentialism by Greg McKeown. How is this different from focus? I believe we need to set what the focus is first, then we can make sure all our actions work towards our goal.&lt;/p&gt;

&lt;p&gt;Greg McKeown's diagram explains his concept perfectly. It's what happens when we put all our energy into moving in one direction. We'll get further than trying to spread our attention across too many things.&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%2Fwmujp6gfln5cnys1elub.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%2Fwmujp6gfln5cnys1elub.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What does that mean for us?&lt;/p&gt;

&lt;p&gt;If we're trying to reach product-market fit, we're probably trying different features that resonate with customers, building things fast. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should we worry about infrastructure? &lt;/li&gt;
&lt;li&gt;Should we even be worrying about scaling? &lt;/li&gt;
&lt;li&gt;Should we be worrying about the cleanest and most maintainable code? &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If doing those things slow us down, and we can't even get one customer because our time is spent worrying about things that are not our core focus, then we're wasting time and resources. We're solving a problem we don't have and may never have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Release small, release often
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;From a developer's perspective, large releases are downright terrifying.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So much code going into production can go all-kinds-of wrong, not necessarily at first, but in the hours and days to come. Having lots of small releases reduces the risk of introducing bugs. But there's another perspective.&lt;/p&gt;

&lt;p&gt;When we're trying to get early adopters, even having a little tiny piece of a product in hours or days is better than having the finished super mammoth product in months or years! If we can get the product into someone's hands and get feedback, we'll learn fast from a technical point of view and from a product one. With this fast feedback, we can decide whether we should keep walking down that road to this big goal, or whether we should pivot or quit while we're ahead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't waste time on developer problems
&lt;/h2&gt;

&lt;p&gt;I believe in hiring tools like hiring people.&lt;/p&gt;

&lt;p&gt;If we have a software engineer on our team, they're there to build products unique to us. So don't be afraid to allow our software engineers to leverage tools for generic problems, whether paid or free. Using these tools is almost like hiring the person who built them, but for much lower costs.&lt;/p&gt;

&lt;p&gt;But you might think, why have a developer use tools when they should be able to build them?&lt;/p&gt;

&lt;p&gt;For example, very few people would consider building their own payment gateway directly with banks, unless our focus is on becoming the next Stripe or Paddle! &lt;/p&gt;

&lt;p&gt;So for instance, instead of letting our developer build a payment gateway integration from scratch to collect payments, which apparently took &lt;a href="https://www.reddit.com/r/EntrepreneurRideAlong/comments/182yj72/heres_how_stripe_a_50_billion_company_got_its/" rel="noopener noreferrer"&gt;Stripe 6 months&lt;/a&gt;, we can set up a beautiful payment UI in a day or less using payment services that do it for us. The difference?&lt;/p&gt;

&lt;p&gt;Cost using payment SaaS company = transaction costs + 1 developer day&lt;br&gt;
Cost using our own developer = 120 developer days + tonnes of maintenance time!&lt;/p&gt;

&lt;p&gt;Once we reach a scale where we're now cost optimising, then we can worry about all these other things. We may indeed have a cheaper way of handling infrastructure, like DHH did when he left AWS. But in the meantime, we as developers have to frequently question, am I working on a developer problem and is there a tool I can use now so I have time to work on business problems?&lt;/p&gt;

&lt;p&gt;Basically, don't reinvent the wheel, at least, not prematurely. Build things that are unique to the business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hire tools to avoid wasting time on maintenance
&lt;/h2&gt;

&lt;p&gt;Some people say real developers roll out their own authentication. But let's take authentication as an example. It's not just a user table. It's managing emails. It's handling email bouncebacks. It's SMS and 2FA if we really want to protect customer data. It's handling customer passwords and secrets correctly.&lt;/p&gt;

&lt;p&gt;Alternatively, we can use open source tools or hire third-party tools from companies. Really, these companies could have dozens if not hundreds of developers that work on the solution they provide. When we use these tools, we're hiring these people! We're hiring their expertise. &lt;/p&gt;

&lt;p&gt;For example, with Okta or Auth0, they keep up with security best practices and make improvements to their product all the time. If there's a security patch they need to apply, chances are, without us even knowing about it, those changes will be rolled out and working for us. Unless we're trying to be the next authentication provider, why not leverage these companies and tools?&lt;/p&gt;

&lt;p&gt;It's the same with infrastructure, which is a hard one for me as I really like my servers. It's no problem to set up Let's Encrypt, write a GitHub Action and have full CI/CD. It takes me at most, an hour or two if that from scratch. But I also have to admit, what about DDoS attacks, and handling that? What about server patches, which will be needed at some point? What about scaling the infra to meet customer needs? These are all things I'd have to worry about by making the decision to have to maintain the thing. &lt;/p&gt;

&lt;p&gt;Alternatively, I could just leverage infrastructure-as-a-service that does it all for me, and some of those have free tiers which is just enough to carry us through until we start getting enough users to worry about how to deal with costs at scale. &lt;/p&gt;

&lt;p&gt;So we have a choice. We can have a developer spending weeks if not months per year worrying about these things that our customers didn't hire us for. We might make an allowance here or there, but we really have to decide if we can afford to or not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Monitoring as QA
&lt;/h2&gt;

&lt;p&gt;I've worked with some of the best QA Engineers, and they've literally prevented hundreds of bugs going into production, if not thousands. However, if we're starting out and don't have enough customers yet, hiring a great QA Engineer is not going to be our focus. So what can we do instead?&lt;/p&gt;

&lt;p&gt;Using something like Bugsnag, an observability platform that checks for bugs and issues users experience in production means we use our monitoring as QA. As soon as there's an unhandled error, Bugsnag alerts us and gives us a digest of issues. This doesn't mean not testing as a developer, because even for a relatively simple project, I used TDD to write the core logic. However, it means not spending huge amounts of time trying to QA our product and slowing down releases. Besides, a QA Engineer would probably do a better job of testing our code anyway, especially with a pair of fresh eyes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Write down your principles
&lt;/h2&gt;

&lt;p&gt;If we're the founding engineer or CTO, we should write down our principles so that when we onboard new developers, they don't have to ask us millions of questions to make decisions. They'll have the autonomy to just move ahead with things. &lt;/p&gt;

&lt;p&gt;This saves us time, because we share our core principles with our team, and it saves them time too. If there's no one else working with us, then it might not be a priority to care about, but there's nothing stopping us from having a quick list of 3 or 4 things in a sentence or less. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a third-party tool with a free tier &amp;gt; open-source &amp;gt; roll out own.&lt;/li&gt;
&lt;li&gt;Use existing tools unless one doesn't exist.&lt;/li&gt;
&lt;li&gt;Use modular monolith over separate service.&lt;/li&gt;
&lt;li&gt;Scale vertically before worrying about scaling horizontally.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Have transparent roadmaps
&lt;/h2&gt;

&lt;p&gt;Roadmaps shouldn't just be for the tech team but for the whole company. Everyone should be able to take a look at it and know what we are working on right now, what's been done, and what's to come.&lt;/p&gt;

&lt;p&gt;Why? Two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It gives our team focus. If a piece of work is in progress on the roadmap, but we're working on other things, we have to ask ourselves, why?&lt;/li&gt;
&lt;li&gt;If we're talking to current and potential customers, it'll make the conversations a lot easier, as we'll know what's in the pipeline.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now whether we share this roadmap with the outside world is up to us, but some companies have done it with great effect! Take Supermetrics. Their customers know their roadmap, what integrations are being built and they can even offer suggestions for tools to integrate 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%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Farxhlmx6te4ltobrm2cw.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%2Farxhlmx6te4ltobrm2cw.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose what we can be productive in
&lt;/h2&gt;

&lt;p&gt;I love using new tools. I can't wait to learn Zig and Rust. I want to work with Go on a product with customers, not just personal projects. I know everyone is clearly using Next.js. After years of working with Vue.js, knowing React is still the rage, I feel like I should pick it up again, but do we really need to build a single-page application? Can we use a Go or Node.js server with some HTMX and Alpine.js sprinkled on top? If we go down the React route, then we need to create an API. Do we need an API? Then we need validation on the client and the backend. Can we just have validation on the backend? Do we need two separate Git[Hub|Lab]/Bitbucket repositories? Do we need Docker? I mean, do we really? We might even ask, do we even need a server? Could we just have something like Firebase and just a static app hosted on Netlify or Vercel or something? Could we just use functions with functions as a service?&lt;/p&gt;

&lt;p&gt;Depending on our skills and core competencies, starting with what we know means we can get productive fast. But sometimes, picking up a framework that does most of the work for us, especially if we know we'll need it will make our life a lot simpler, if we're willing to give ourselves a small amount of time to learn it upfront. I did this with HTMX like David Guillot did. But if we have one day to get that feature or part of that feature released, and we need to use what we know, that's probably the simplest and most productive thing that we can do, even if it's not the most efficient.&lt;/p&gt;

&lt;p&gt;Now if we have all the time in the world, we can make the thing as complicated as we like: &lt;a href="https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition" rel="noopener noreferrer"&gt;https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Make it work, make it right, maybe make it fast
&lt;/h2&gt;

&lt;p&gt;There's a saying, hindsight is 20/20. We know more about the project at the end rather than at the beginning. And maybe many of us have many things we'd do differently if we could work on a project again. So building the thing quickly gives us exactly that, hindsight. &lt;/p&gt;

&lt;p&gt;Instead of trying to code a feature properly the first time, unless we know exactly what we're doing, get the thing built, then make it right. If we try to build something properly up front, not only do we risk building something that the customer might not use, but we risk wasting time on building it because we make mistakes anyway the first time round and spend longer doing so.&lt;/p&gt;

&lt;h2&gt;
  
  
  Forget ceremonies and the processes
&lt;/h2&gt;

&lt;p&gt;"Agile processes" are the rage, but one of the fundamental principles from the Founding Fathers of the Agile Manifesto themselves is optimise for &lt;a href="https://agilemanifesto.org/" rel="noopener noreferrer"&gt;"individuals and interactions over processes&lt;/a&gt; and tools". &lt;/p&gt;

&lt;p&gt;I've found that it's better to just take regular intervals to identify what's the focus, then double down and get back to building and doing whatever activity is needed to grow the business. So I wouldn't worry about sprints, stand-ups, ceremonies and story points. If they help us build software faster, by all means, but chances are, we don't need to do any of that, but instead, can do things that work for us.&lt;/p&gt;

&lt;h2&gt;
  
  
  Push to prod
&lt;/h2&gt;

&lt;p&gt;Unless it's critical for us not to do so, push directly to prod. If we have to do manual deployments, we will find ourselves trying to avoid deployments all the time, and so our releases will get bigger, slower and more risky.&lt;/p&gt;

&lt;p&gt;"And sacrilege. Pushing directly to production is irresponsible!" Or is it? If we're doing something that is sensitive, like working with payments or data, yes, have our test environments. We need to use our judgement of course, but if we can take away barriers to deploy to production, then we make it easier, not just to release features, but to fix bugs which will certainly happen at some point anyway.&lt;/p&gt;

&lt;p&gt;If Michael Bryzek can do it with a company processing thousands of transactions per day on a mature website and platform, we can do it for our start-ups if there's very little risk.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=z-ATZTUgaAo" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=z-ATZTUgaAo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, we need to test our features before the push.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure your product usage
&lt;/h2&gt;

&lt;p&gt;We can interview our customers, which is a great idea. But what customers say and do are not always the same. When they have the product in their hand, using something like Hotjar or another tool to measure how the product is used will give us real-time insights. If we thought a set of features would help our users, and then after release, realised no one uses them, we can stop wasting time building on that feature even more or approach the feature from a different angle or move on to something else.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make IntelliSense your friend
&lt;/h2&gt;

&lt;p&gt;While I love TypeScript, for a new tool and hopefully product, I used JavaScript with JSDoc. It allows me to build quickly, but with JSDoc I get IntelliSense helping me to build the tool. In fact, after spending 2 hours writing the core logic of feature v0 for a product, I took the JSDoc comments, gave it to Claude.ai, then I asked it to write me the glue code. And it did! &lt;/p&gt;

&lt;p&gt;When we optimise for IntelliSense, it's like documentation to help us write our code in real time without ever having to come out of the IDE. And writing code becomes faster at that too!&lt;/p&gt;

&lt;h2&gt;
  
  
  Document your tools
&lt;/h2&gt;

&lt;p&gt;We all are leveraging a suite of different tools. If we're fortunate to get new hires and developers, they'll start introducing tools of their own. Before we know it, we've got overlap, and people don't know where to find stuff. Even if we're a one-person band, we might even forget all the tools we're using.&lt;/p&gt;

&lt;p&gt;For me, it's things like analytics, email hosting, domain hosting, uptime monitoring. I even used a README to document it. We can use whatever we like. That saves time asking, what are we using.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finally, don't stress
&lt;/h2&gt;

&lt;p&gt;Stress prevents creativity. While we just need to get things done, sometimes creativity and realising there's a simpler, faster and better way to do something versus the way we've always done it can be beneficial. So avoid deadlines, especially if they create stress. Instead, we should try to choose the feature or work that we think will have the biggest impact on our goal that can be achieved in the fastest possible time, and then go do that.&lt;/p&gt;

</description>
      <category>startup</category>
      <category>principles</category>
      <category>engineer</category>
      <category>productivity</category>
    </item>
    <item>
      <title>How to dump and restore a Postgres DB with new table ownership</title>
      <dc:creator>Gemma Black</dc:creator>
      <pubDate>Thu, 14 Mar 2024 08:20:13 +0000</pubDate>
      <link>https://forem.com/gemmablack/how-to-dump-and-restore-postgres-db-with-new-table-ownership-3h8j</link>
      <guid>https://forem.com/gemmablack/how-to-dump-and-restore-postgres-db-with-new-table-ownership-3h8j</guid>
      <description>&lt;p&gt;I've used &lt;a href="https://www.mysql.com/" rel="noopener noreferrer"&gt;MySQL&lt;/a&gt; for years. But recently, I found myself working with &lt;a href="https://www.postgresql.org/" rel="noopener noreferrer"&gt;PostgreSQL&lt;/a&gt; and simple things like dumping and restoring a database are different enough that I decided to document the process. It's straightforward enough once I knew how.&lt;/p&gt;

&lt;p&gt;So first, I'll do a quick overview and then share the explanations afterwards.&lt;/p&gt;

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

&lt;p&gt;There are several ways to dump and restore data in Postgres if you have more specialist needs. I've chosen one almost analogous to how I would do it with MySQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;You need the correct privileges to both &lt;a href="https://www.postgresql.org/docs/current/backup-dump.html" rel="noopener noreferrer"&gt;dump&lt;/a&gt; the database and recreate all the database objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Set the user credentials
&lt;/h3&gt;

&lt;p&gt;Instead of passing options to &lt;code&gt;pg_dump&lt;/code&gt; and &lt;code&gt;psql&lt;/code&gt;, we can also set up our access credentials as environment variables. There are more &lt;a href="https://www.postgresql.org/docs/current/libpq-pgpass.html" rel="noopener noreferrer"&gt;secure alternatives&lt;/a&gt; to handling database access secrets though.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGHOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;host&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGUSER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;user&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PGPASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;password&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Dumping the data
&lt;/h3&gt;

&lt;p&gt;Notice how we dump the data with the &lt;code&gt;--no-privileges&lt;/code&gt; and &lt;code&gt;--no-owner&lt;/code&gt;. Don't include this if you want to keep the ownership as is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pg_dump &lt;span class="se"&gt;\ &lt;/span&gt;                                                                         
  &lt;span class="nt"&gt;--no-privileges&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--no-owner&lt;/span&gt; &lt;span class="se"&gt;\ &lt;/span&gt;
  dbname &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dbname.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Creating the new database
&lt;/h3&gt;

&lt;p&gt;Create a new database from a template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"CREATE DATABASE newdb TEMPLATE template0;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Restoring the data
&lt;/h3&gt;

&lt;p&gt;Notice I used &lt;code&gt;--single-transaction&lt;/code&gt; to import the data. If you don't mind importing the data where there are some errors, remove that option.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
psql &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--single-transaction&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    new_db &amp;lt; dbname.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Changing table ownership
&lt;/h3&gt;

&lt;p&gt;Finally, when working with MySQL, I don't have to worry about table ownership. I just use grants and privileges. But when you import data in Postgres, it assumes the owner is the user associated with the connection. One way I've seen how to change table ownership to one we want is to generate the &lt;code&gt;ALTER&lt;/code&gt; commands and then run that.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql newdb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then within the &lt;code&gt;psql&lt;/code&gt; shell, we create the &lt;code&gt;ALTER&lt;/code&gt; commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## :psql &amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="s1"&gt;'ALTER TABLE '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; t.tablename &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;' OWNER TO new_owner;'&lt;/span&gt; 
 from  pg_tables t
 where t.tableowner &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s1"&gt;'rdsadmin'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It will print out something like the following, which you will need to copy-paste, and run within the shell again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;## :psql &amp;gt;&amp;gt;&lt;/span&gt;

ALTER TABLE &lt;span class="nb"&gt;groups &lt;/span&gt;OWNER TO new_owner&lt;span class="p"&gt;;&lt;/span&gt;
ALTER TABLE images OWNER TO new_owner&lt;span class="p"&gt;;&lt;/span&gt;
ALTER TABLE &lt;span class="nb"&gt;jobs &lt;/span&gt;OWNER TO new_owner&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;ul&gt;
&lt;li&gt;&lt;p&gt;We've dumped the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We've restored it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;And we've changed the table ownership to what we want.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, I'll share a few things I found in the documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes on how the dump and restore work
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Libpq
&lt;/h3&gt;

&lt;p&gt;Libpq is the "C application programmer's interface to PostgreSQL". So I needed to install &lt;a href="https://www.postgresql.org/docs/9.5/libpq.html" rel="noopener noreferrer"&gt;Libpq&lt;/a&gt; before being able to run some commands on my local environment. For me on MacOS, it was not installed with Postgres and had to be done separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Table locks
&lt;/h3&gt;

&lt;p&gt;With MySQL, dumping the database with table locks means users may experience slow responses or even downtime. To prevent table locks in MySQL, we can do the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mysqldump &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--single-transaction&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--skip-lock-tables&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    dbname &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; dbname.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--single-transaction&lt;/code&gt; tells the &lt;code&gt;mysqldump&lt;/code&gt; to "put everything into a transaction", and "read the database in the current state and create a consistent data dump". But it will still lock the tables unless we provide &lt;code&gt;--skip-lock-tables&lt;/code&gt;. That way, we get a consistent data output and no table locks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;↗️ Run mysqldump without locking the tables - &lt;a href="https://mysqldump.guru/run-mysqldump-without-locking-the-tables.html" rel="noopener noreferrer"&gt;https://mysqldump.guru/run-mysqldump-without-locking-the-tables.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In Postgres, this is not a problem as "dumps created by pg_dump are internally consistent, that is, updates to the database while pg_dump is running will not be in the dump. pg_dump does not block other operations on the database while it is working." Therefore, no extra options are needed. Of course, there are exceptions.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;↗️ Chapter 9. Backup and Restore - &lt;a href="https://www.postgresql.org/docs/7.2/backup.htm" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/7.2/backup.htm&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Using options instead of environment variables for connection settings
&lt;/h3&gt;

&lt;p&gt;Similar to MySQL, we have &lt;code&gt;-h host&lt;/code&gt;, &lt;code&gt;-p port&lt;/code&gt; , but we have &lt;code&gt;-W password&lt;/code&gt; for the prompt and a capital &lt;code&gt;U&lt;/code&gt; for &lt;code&gt;-U user&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Removing ownership
&lt;/h3&gt;

&lt;p&gt;If you want the table ownership and privileges to remain the same, providing &lt;code&gt;--no-privileges&lt;/code&gt; and &lt;code&gt;--no-owner&lt;/code&gt; is not necessary. However, without this, you get the following SQL in your SQL dump:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;CREATE SCHEMA abc&lt;span class="p"&gt;;&lt;/span&gt;

ALTER SCHEMA abc OWNER TO existing_user&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Database Templates
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.postgresql.org/docs/current/manage-ag-templatedbs.html" rel="noopener noreferrer"&gt;Database Templates&lt;/a&gt; in Postgres is a foreign concept to me in MySQL. However, when we do, &lt;code&gt;CREATE DATABASE&lt;/code&gt; in Postgres, it's an alias for &lt;code&gt;CREATE DATABASE dbname TEMPLATE template1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, there are two templates by default. &lt;code&gt;template0&lt;/code&gt; and &lt;code&gt;template1&lt;/code&gt;. In the earlier example, I used &lt;code&gt;template0&lt;/code&gt;, but why?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;template1&lt;/code&gt; can be amended so you can create a new database with your customisations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;template0&lt;/code&gt; doesn't have any customisations. So it's a pure, unadulterated database that you can clone and copy.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  All-or-nothing imports
&lt;/h3&gt;

&lt;p&gt;Something interesting we can do in Postgres is ensure our imported data is consistent. If there are any errors when importing the dumped file, it will roll back all changes. To do this, we use &lt;code&gt;--single-transaction&lt;/code&gt; on the import:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;psql &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--single-transaction&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &amp;lt; dumpfile.sql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note: "When using this mode, be aware that even a minor error can rollback a restore that has already run for many hours. However, that might still be preferable to manually cleaning up a complex database after a partially restored dump." - &lt;a href="https://www.postgresql.org/docs/current/backup-dump.html#:~:text=When%20using%20this%20mode%2C%20be,after%20a%20partially%20restored%20dump." rel="noopener noreferrer"&gt;postgresql.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  That wasn't so bad
&lt;/h2&gt;

&lt;p&gt;MySQL and Postgres are similar but they're &lt;strong&gt;not&lt;/strong&gt; the same. Hopefully dumping and restoring in Postgres will be simpler (even if it's just for me) in future.&lt;/p&gt;

</description>
      <category>mysql</category>
      <category>postgres</category>
      <category>backups</category>
    </item>
    <item>
      <title>NodeJS: 4.8x faster if we go back to callbacks!</title>
      <dc:creator>Gemma Black</dc:creator>
      <pubDate>Sat, 10 Feb 2024 10:08:00 +0000</pubDate>
      <link>https://forem.com/gemmablack/nodejs-347x-faster-if-we-go-back-to-callbacks-1h4m</link>
      <guid>https://forem.com/gemmablack/nodejs-347x-faster-if-we-go-back-to-callbacks-1h4m</guid>
      <description>&lt;p&gt;Yeah, I said it!&lt;/p&gt;

&lt;p&gt;Callbacks are &lt;strong&gt;4.8x&lt;/strong&gt; faster when running them parallel over async/await in parallel. And only &lt;strong&gt;1.9x&lt;/strong&gt; faster when we run sequential callbacks.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I've modified this article somewhat after I got some helpful and kind comments about my dodgy test. 😂🙏&lt;/p&gt;

&lt;p&gt;Thank you to &lt;a href="https://twitter.com/ricardoplopes" rel="noopener noreferrer"&gt;Ricardo Lopes&lt;/a&gt; and &lt;a href="https://github.com/baublet" rel="noopener noreferrer"&gt;Ryan Poe&lt;/a&gt; for taking the time to steer the benchmarks in the right direction. My first faux pas was I wasn't actually waiting for the execution of the code to finish, which crazily skewed the results. The second was I was comparing parallel to sequential runtimes, which make the benchmarks worthless.&lt;/p&gt;

&lt;p&gt;So this is round 2 which addresses my initial errors. Previously, I said:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NodeJS: 34.7x faster if we go back to callbacks! I shouldn't have believed it. 🤣&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not as impressive as my bad benchmarks before (and see comments for context), but still a sizeable difference.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what exactly did I test?
&lt;/h2&gt;

&lt;p&gt;I compared callbacks to promises and async/await when reading a file, 10,000 times. And maybe that's a silly test but I wanted to know, which is faster at I/O.&lt;/p&gt;

&lt;p&gt;Then I finally compared callbacks in Node.js to Go!&lt;/p&gt;

&lt;p&gt;Now, guess who won?&lt;/p&gt;

&lt;p&gt;I won't be mean. &lt;strong&gt;TLDR&lt;/strong&gt;. Golang!&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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1707581413134%2Fafcda514-8da2-41e6-ae1b-d45b2e5e6bba.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%2Fcdn.hashnode.com%2Fres%2Fhashnode%2Fimage%2Fupload%2Fv1707581413134%2Fafcda514-8da2-41e6-ae1b-d45b2e5e6bba.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Lower is better. Results are in &lt;code&gt;ms&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, true benchmarkers? Go easy on me on this one. But please do leave your comments to make me a better person.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Everyone keeps saying that Node.js is slow!
&lt;/h2&gt;

&lt;p&gt;And it bugs me out.&lt;/p&gt;

&lt;p&gt;Because what does slow mean? As with all benchmarks, mine is contextual.&lt;/p&gt;

&lt;p&gt;I started reading about the &lt;a href="https://nodejs.org/en/guides/event-loop-timers-and-nexttick" rel="noopener noreferrer"&gt;Event Loop&lt;/a&gt;, just to even begin to understand &lt;a href="https://www.builder.io/blog/visual-guide-to-nodejs-event-loop" rel="noopener noreferrer"&gt;how it works&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But the main thing I've understood is that Node.js passes I/O tasks onto a queue that sits outside the main Node.js executable thread. This queue runs on &lt;a href="https://libuv.org/" rel="noopener noreferrer"&gt;pure C&lt;/a&gt;. A number of threads could potentially handle these I/O operations. And that's where Node.js can shine, handling I/O.&lt;/p&gt;

&lt;p&gt;Promises, however, get handled in the main, single executable thread. And async/await, is well, promises but now with blocking added.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.builder.io/blog/visual-guide-to-nodejs-event-loop" 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%2Fcdn.builder.io%2Fapi%2Fv1%2Fimage%2Fassets%252FYJIGb4i01jvw0SRdL5Bt%252F6b288555862049b4b5cd7f19e2ae909f%3Fwidth%3D705" alt="Event loop consisting of 6 different queues."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  So are callbacks faster than promises?
&lt;/h2&gt;

&lt;p&gt;Let's put it to the test.&lt;/p&gt;

&lt;p&gt;First off. My &lt;em&gt;machine&lt;/em&gt;! Complements of working with &lt;a href="https://kammadata.com/" rel="noopener noreferrer"&gt;Kamma&lt;/a&gt;. It's important to note what resources we're working with. Plenty memory and CPU.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;MacBook Pro &lt;span class="o"&gt;(&lt;/span&gt;14-inch, 2021&lt;span class="o"&gt;)&lt;/span&gt;
Chip      Apple M1 Pro
Memory    32 GB
Cores     10
NodeJS    v20.8.1
Go        1.21.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So we have a &lt;code&gt;text.txt&lt;/code&gt; file with an &lt;em&gt;original&lt;/em&gt; message, &lt;code&gt;Hello, world&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, world"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; text.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we'll read this text file using native Node.js, which means, zero node module dependencies because we don't want to drag the speed down with the heaviest objects in the universe.&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%2F7om917fn62m5s6eowdx9.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%2F7om917fn62m5s6eowdx9.png" alt="Heaviest Objects In The Universe : r/ProgrammerHumor"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Callbacks
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Parallel callbacks
&lt;/h4&gt;

&lt;p&gt;First, let's start with &lt;em&gt;parallel&lt;/em&gt; callbacks. I'm interested in how quickly the same file can be read as quickly as possible, all at once. And what's faster than parallel?&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;// &amp;gt; file-callback-parallel.test.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with callback parallel&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;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;10000&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&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;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
            &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;done&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Sequential callbacks
&lt;/h4&gt;

&lt;p&gt;Second, we have callbacks again, but sequential (or rather blocking). I'm interested in how quickly the same file can be read sequentially. Having not done callbacks calling callbacks for ages, this was fun to try again. Albeit, it doesn't look pretty.&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;// &amp;gt; file-callback-blocking.test.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs&lt;/span&gt;&lt;span class="dl"&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;read&lt;/span&gt; &lt;span class="o"&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;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&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;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&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="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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;i&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;return&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nf"&gt;read&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;callback&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with callback blocking&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;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;read&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;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Async/Await
&lt;/h3&gt;

&lt;p&gt;Then we have async/await. My favourite way of working with Nodejs.&lt;/p&gt;

&lt;h4&gt;
  
  
  Parallel async/await
&lt;/h4&gt;

&lt;p&gt;It's as parallel as I can get with async/await. I load all the &lt;code&gt;readFile&lt;/code&gt; operations into an array and await them all using &lt;code&gt;Promise.all&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="c1"&gt;// &amp;gt; file-async-parallel.test.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with async parallel&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allFiles&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;10000&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;allFiles&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&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="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allFiles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;allFiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Sequential Async/Await
&lt;/h4&gt;

&lt;p&gt;This was the easiest and most concise one to write.&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;// &amp;gt; file-async-blocking.test.mjs&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with async blocking&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;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;10000&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="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Promises
&lt;/h3&gt;

&lt;p&gt;Finally, we have promises without async/await. I've long stopped using them in favour of &lt;code&gt;async/await&lt;/code&gt; but I was interested in whether they were performant or not.&lt;/p&gt;

&lt;h4&gt;
  
  
  Parallel promises
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// &amp;gt; file-promise-parallel.test.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with promise parallel&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;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;allFiles&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;10000&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="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;allFiles&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&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="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allFiles&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;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;10000&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="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;allFiles&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nf"&gt;done&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;h4&gt;
  
  
  Sequential promises.
&lt;/h4&gt;

&lt;p&gt;Again, we want to wait for the execution of all &lt;code&gt;readFile&lt;/code&gt; operations.&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;// &amp;gt; file-promise-blocking.test.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs/promises&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with promises blocking&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;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&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;10000&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="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
                &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nf"&gt;done&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And voila! Results 🎉! I even ran it a few times to get a better reading.&lt;/p&gt;

&lt;p&gt;I ran each test by doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;
node &lt;span class="nt"&gt;--test&lt;/span&gt; &amp;lt;file&amp;gt;.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reading a file 10,000 times with callbacks is over 5.8x faster than with async/await in parallel!&lt;/strong&gt; It's also 4.7x faster than with promises in parallel!&lt;/p&gt;

&lt;p&gt;So, in Node.js land, callbacks &lt;em&gt;are&lt;/em&gt; more performant!&lt;/p&gt;

&lt;h2&gt;
  
  
  Now is Go faster than Node.js?
&lt;/h2&gt;

&lt;p&gt;Well, I don't write in Go, so this may be truly terrible code because I asked ChatGPT to help me and yet, it &lt;em&gt;seems&lt;/em&gt; pretty decent.&lt;/p&gt;

&lt;p&gt;Hey ho. Let's go. Our Golang code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"io/ioutil"&lt;/span&gt;
    &lt;span class="s"&gt;"time"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;startTime&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;ioutil&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ReadFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"./text.txt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error reading file: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s"&gt;"Hello, world"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"File content mismatch: got"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;", want Hello, world"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Since&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;startTime&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Test execution time: %v&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;duration&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 we run it as so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;go run main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the results?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Test execution &lt;span class="nb"&gt;time&lt;/span&gt;: 58.877125ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🤯 Go is 4.9x faster than Node.js using sequential callbacks. Node.js only comes close with parallel execution.&lt;/p&gt;

&lt;p&gt;Node.js Async/await is 9.2x slower than Go.&lt;/p&gt;

&lt;p&gt;So yes. Node.js is slower. Still, 10,000 files in sub 300ms isn't to be scoffed at. But I've been humbled by Go's speediness!&lt;/p&gt;

&lt;h2&gt;
  
  
  Now just a side note. Do I have bad benchmarks?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;I really did have terrible Benchmarks. Thank you again to Ricardo and Ryan.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, I did. Hopefully now they're better.&lt;/p&gt;

&lt;p&gt;But you may ask, who's really going to read the same file, over and over again? But for a relative test between things, I hope it's a helpful comparison.&lt;/p&gt;

&lt;p&gt;I also don't know how many threads Node.js is using.&lt;/p&gt;

&lt;p&gt;I don't know how my CPU cores affect Go vs Node.js performance.&lt;/p&gt;

&lt;p&gt;I could just rent an AWS machine with one core and compare.&lt;/p&gt;

&lt;p&gt;Is it because I'm on Mac M1?&lt;/p&gt;

&lt;p&gt;How would Node.js perform on a Linux or...Windows? 😱&lt;/p&gt;

&lt;p&gt;And there's the practicality of, yes, reading a file is one thing, but at some point, you have to wait anyway for the file to be read to do something with the data in the file. So, speed on the main thread is still pretty important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Now, do you really want to use callbacks?
&lt;/h2&gt;

&lt;p&gt;I mean, do you really, really want to?&lt;/p&gt;

&lt;p&gt;I don't know. I definitely don't want to tell anyone what to do.&lt;/p&gt;

&lt;p&gt;But I like the clean syntax of async/awaits.&lt;/p&gt;

&lt;p&gt;They look better.&lt;/p&gt;

&lt;p&gt;They read better.&lt;/p&gt;

&lt;p&gt;I know better is subjective here but I remember callback-hell, and I was grateful when promises came into existence. It made Javascript bearable.&lt;/p&gt;

&lt;p&gt;Now, Golang is clearly faster than Node.js at its optimum, with callbacks, and with async/await, by 9.2x! So if we want good readability and performance, Golang is the winner. Although, I'd love to learn how Golang looks under the hood.&lt;/p&gt;

&lt;p&gt;Anywho. This was fun. It was more of an exercise to help me understand how callbacks and I/O work in the Event Loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  So to sign out
&lt;/h2&gt;

&lt;p&gt;Is Node.js slow? Or are we just using Node.js on slow mode?&lt;/p&gt;

&lt;p&gt;Probably where performance matters, Golang is worth the jump. I'll certainly be looking more at using Golang in future.&lt;/p&gt;




&lt;h2&gt;
  
  
  Updates
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sequential promises
&lt;/h3&gt;

&lt;p&gt;If I rewrite it as below, I'm still using callbacks 😬 but at least it's sequential:&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;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;fs&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;node:fs/promises&lt;/span&gt;&lt;span class="dl"&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;read&lt;/span&gt; &lt;span class="o"&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;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./text.txt&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Hello, world&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="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&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;i&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;return&lt;/span&gt; &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;

            &lt;span class="nf"&gt;read&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;callback&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="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reading file 10,000 times with promises blocking&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;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;done&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;read&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;done&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 didn't include the above in the benchmarks but it is slower and not surprisingly so at &lt;code&gt;532.777667ms&lt;/code&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>NodeJS: We can run tests natively!</title>
      <dc:creator>Gemma Black</dc:creator>
      <pubDate>Wed, 07 Feb 2024 10:37:55 +0000</pubDate>
      <link>https://forem.com/gemmablack/nodejs-we-can-run-tests-natively-508c</link>
      <guid>https://forem.com/gemmablack/nodejs-we-can-run-tests-natively-508c</guid>
      <description>&lt;p&gt;Once upon a time, there was Mocha.&lt;/p&gt;

&lt;p&gt;Then there was Jasmine. Or perhaps it was the other way around. But Mocha was first for me.&lt;/p&gt;

&lt;p&gt;There was Jest.&lt;/p&gt;

&lt;p&gt;Now there's Vitest.&lt;/p&gt;

&lt;p&gt;If you want to write tests for your Nodejs app, you're spoilt for choice. You just run &lt;code&gt;npm install&lt;/code&gt; for the pleasure.&lt;/p&gt;

&lt;p&gt;Some worked well with Grunt and Gulp.&lt;/p&gt;

&lt;p&gt;Others worked great with Webpack.&lt;/p&gt;

&lt;p&gt;Now there are those that work perfectly with Vite.&lt;/p&gt;

&lt;p&gt;But unless we're doing some compilation for the front end or we're working with Typescript, why aren't we using NodeJS's native test runner? It's been around since version 18! That's April 2022. Almost 2 years in!&lt;/p&gt;

&lt;p&gt;So what does it look like? 💯 zero node modules required:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;And straight from the &lt;a href="https://nodejs.org/docs/latest/api/test.html#test-runner" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// &amp;gt; node test.mjs&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;assert&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:assert&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;synchronous passing test&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;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Assertions? They're native.&lt;/p&gt;

&lt;p&gt;Test runners? Also native.&lt;/p&gt;

&lt;p&gt;The output? Delightful. 🤩&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="err"&gt;✔&lt;/span&gt; &lt;span class="nx"&gt;synchronous&lt;/span&gt; &lt;span class="nx"&gt;passing&lt;/span&gt; &lt;span class="nf"&gt;test &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.618667&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;tests&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;suites&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;pass&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;fail&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;cancelled&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;skipped&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;todo&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="nx"&gt;ℹ&lt;/span&gt; &lt;span class="nx"&gt;duration_ms&lt;/span&gt; &lt;span class="mf"&gt;4.6255&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can even run tests spanning multiple files. As long as the file ends with an acceptable glob pattern like &lt;code&gt;*.test.mjs&lt;/code&gt; , you can run the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Default globs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/*.test.?(c|m)js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/*-test.?(c|m)js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/*_test.?(c|m)js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/test-*.?(c|m)js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/test.?(c|m)js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;**/test/**/*.?(c|m)js&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;We even have &lt;strong&gt;watch mode&lt;/strong&gt; for goodness' sake. How did I not know about this?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--test&lt;/span&gt; &lt;span class="nt"&gt;--watch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Code coverage? Yes. 😯&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# node --test --experimental-test-coverage&lt;/span&gt;

ℹ tests 6
ℹ suites 2
ℹ pass 6
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 54.586833
ℹ start of coverage report
ℹ &lt;span class="nt"&gt;--------------------------------------------------------------&lt;/span&gt;
ℹ file          | line % | branch % | funcs % | uncovered lines
ℹ &lt;span class="nt"&gt;--------------------------------------------------------------&lt;/span&gt;
ℹ test.mjs      | 100.00 |   100.00 |  100.00 | 
ℹ test.test.mjs | 100.00 |   100.00 |  100.00 | 
ℹ &lt;span class="nt"&gt;--------------------------------------------------------------&lt;/span&gt;
ℹ all files     | 100.00 |   100.00 |  100.00 |
ℹ &lt;span class="nt"&gt;--------------------------------------------------------------&lt;/span&gt;
ℹ end of coverage report
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;describe/it&lt;/code&gt; blocks? Yes, too! 🤯&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;A thing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should be ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&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="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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a nested thing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should work&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;assert&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strictEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&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;And it looks beautiful may I add.&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="err"&gt;▶&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nx"&gt;thing&lt;/span&gt;
  &lt;span class="err"&gt;✔&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nf"&gt;work &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.082667&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;✔&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nx"&gt;be&lt;/span&gt; &lt;span class="nf"&gt;ok &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.051292&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;▶&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt; &lt;span class="nx"&gt;thing&lt;/span&gt;
    &lt;span class="err"&gt;✔&lt;/span&gt; &lt;span class="nx"&gt;should&lt;/span&gt; &lt;span class="nf"&gt;work &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.05575&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="err"&gt;▶&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="nx"&gt;nested&lt;/span&gt; &lt;span class="nf"&gt;thing &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.126541&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="err"&gt;▶&lt;/span&gt; &lt;span class="nx"&gt;A&lt;/span&gt; &lt;span class="nf"&gt;thing &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.512584&lt;/span&gt;&lt;span class="nx"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  So why aren't we using more of Nodejs natively?
&lt;/h2&gt;

&lt;p&gt;One reason is that the test runner didn't exist until April 2022, probably when I was already super excited about Vite. Another reason is we rarely just work with pure Nodejs. Normally we work with a frontend framework, be it React or Vue, Typescript, Purescript, or a fully-fledged framework like AdonisJS to make our life easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing our dependencies and relying on native
&lt;/h2&gt;

&lt;p&gt;Having recently fallen foul of dependency hell with an old project, I came to the realisation, that reducing dependencies makes it easier to update a project. Try and fix a Node version 8 script with a dozen node modules that are no longer maintained, and you realise it's probably easier to just write the whole thing again.&lt;/p&gt;

&lt;p&gt;The downside is native Nodejs will be more verbose to write, it will lack the Typescript types that I love so much, and it will be missing the prettier documentation that comes with beautiful frameworks like Nuxt.&lt;/p&gt;

&lt;p&gt;However, if I want my projects to last a long time with a minimum of fuss and maintenance, it might be worth forgoing these shinier tools that abstract away NodeJS's NodeJSness, and just use it with its fullest purity.&lt;/p&gt;

&lt;p&gt;It's not just a NodeJS problem. Every language has this problem. We've developed tools to make our lives easier because changing and improving the underlying language isn't always possible.&lt;/p&gt;

&lt;p&gt;Yet, &lt;a href="https://twitter.com/ricardoplopes" rel="noopener noreferrer"&gt;Ricardo Lopes&lt;/a&gt; proved you can have a sane zero-dependency mini &lt;a href="https://rplopes.hashnode.dev/resist-exploding-complexity" rel="noopener noreferrer"&gt;web application&lt;/a&gt; without even a &lt;code&gt;package.json&lt;/code&gt; written in NodeJS:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"There's a place for complex frameworks and architectures, sure. But for many projects, they may be an overkill."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if we're developing a project that's 1) small, 2) not really going to change much, and 3) only needs NodeJS, why not avoid using any dependencies at all, and go native with &lt;code&gt;node --test&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;I'll sign off with a &lt;a href="https://x.com/flaviocopes?s=20" rel="noopener noreferrer"&gt;Flavio&lt;/a&gt; Tweet in favour of reducing dependencies.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1754784031307084015-865" src="https://platform.twitter.com/embed/Tweet.html?id=1754784031307084015"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1754784031307084015-865');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1754784031307084015&amp;amp;theme=dark"
  }



&lt;/p&gt;

</description>
      <category>node</category>
      <category>testing</category>
    </item>
    <item>
      <title>Making Preons</title>
      <dc:creator>Gemma Black</dc:creator>
      <pubDate>Mon, 25 May 2020 16:47:44 +0000</pubDate>
      <link>https://forem.com/gemmablack/making-preons-2b3j</link>
      <guid>https://forem.com/gemmablack/making-preons-2b3j</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is my first serious article on dev.to. Critique is most welcomed. Nevertheless, I love how-to guides but there are so many good ones out there. Instead, I hope to merely share my experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Story - 2 years and 4 iterations
&lt;/h2&gt;

&lt;p&gt;I loved writing code. I still do.&lt;/p&gt;

&lt;p&gt;In front of my laptop, I was studying yet another project. The design was beautiful as always. All I had to do was make the HTML. But here I was again, writing the navigation, the card elements, the buttons, the grid, the footer. Déjà vu or not, I knew there had to be a way of systemising the creation of websites.&lt;/p&gt;

&lt;p&gt;All I wanted was to build beautiful user interfaces faster without rewriting the same code every time. So I created Preons.&lt;/p&gt;

&lt;p&gt;In this article I share:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The principles I learned along the way&lt;/li&gt;
&lt;li&gt;And how Preons became a css generating system&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Iteration 0 - Tachyons
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The story
&lt;/h3&gt;

&lt;p&gt;It's a bit cheeky saying &lt;a href="https://tachyons.io/docs/"&gt;Tachyons&lt;/a&gt; is the first iteration. I didn't build it. &lt;a href="http://mrmrs.cc/"&gt;Adam Morse&lt;/a&gt; and co &lt;a href="https://github.com/tachyons-css/tachyons"&gt;did&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All I know is I &lt;a href="https://www.collinsdictionary.com/dictionary/english/cotton-on"&gt;cottoned on to it&lt;/a&gt; either at the end of 2017 or the beginning of 2018.&lt;/p&gt;

&lt;p&gt;But this very library changed my thinking about how stylesheets &lt;a href="https://www.thoughtworks.com/insights/blog/good-programer-avoid-being-one"&gt;could&lt;/a&gt; be written. I was doing &lt;a href="http://getbem.com/"&gt;BEM&lt;/a&gt;. And there was a cycle. Every UI element needed both handwritten css and afterwards, HTML.&lt;/p&gt;

&lt;p&gt;Tachyons was different. Tachyons was about writing a stylesheet once, then building the interface with that stylesheet right there in the DOM.&lt;/p&gt;

&lt;p&gt;5 minutes later, I copy-pasted up a nice looking user interface. No CSS written. The hard work went solely into finding components that looked good. A nice header. A fancy footer. And something in between.&lt;/p&gt;

&lt;p&gt;This is what I mocked up again (circa 2020):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tachyons--gemmadlou.repl.co/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0XjfsA_F--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://preons.co/images/tachyons-quick-example.jpg%23boxed" alt="Tachyons in 5 minutes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://repl.it/@gemmadlou/Tachyons#index.html"&gt;edit&lt;/a&gt; on repl.it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Change paddings. Add margins. Increase font sizes right there in the browser. Underneath the hood, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;footer&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"pa4 pa5-l black-70 bt b--black-10"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/footer&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The only thing was, I wanted a different font. A Google font to be exact. And then the colours weren't what I liked. These were the things not in the stylesheet.&lt;/p&gt;

&lt;p&gt;Being able to make these little customisations quickly and easily are important. Not every project has the same vertical rhythm. Not every project needs the same CSS properties let alone values.&lt;/p&gt;

&lt;p&gt;So I did the easiest thing, but the wrong thing. I hardcoded classes into a new stylesheet that "extended" the Tachyons library. But there was a priority problem. Can you spot it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/** 
 * In tachyons.css
 */&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60em&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.aspect-ratio-l&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.aspect-ratio--16x9-l&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;56.25%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nc"&gt;.aspect-ratio--9x16-l&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;177.77%&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="c"&gt;/**
 * Then...in a custom .css
 */&lt;/span&gt;

&lt;span class="nc"&gt;.aspect-ratio--4-3&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;padding-bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;75%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So what happens if I try to have applied different aspect ratios at different breakpoints?&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;class=&lt;/span&gt;&lt;span class="s"&gt;"aspect-ratio--4-3 aspect-ratio--16x9-l"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"some-image.png"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Lesson 1 - Framework or library
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Library&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you use a library, you are in charge of the flow of the application. You are choosing when and where to call the library.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you use a framework, the framework is in charge of the flow. It provides some places for you to plug in your code, but it calls the code you plugged in as needed.&lt;/p&gt;

&lt;p&gt;- &lt;a href="https://www.freecodecamp.org/news/the-difference-between-a-framework-and-a-library-bd133054023f/"&gt;The Difference Between a Framework and a Library&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Updating Tachyons felt like I was using it as a framework rather than as a dependency. To prevent priority issues, you need to insert your new css classes at particular places inside the stylesheet. But then the line is blurred between what is mine and added, and what is Tachyons'.&lt;/p&gt;

&lt;p&gt;Then I thought, maybe it's better instead, not to extend Tachyons, but generate it instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 2 - Use what exists
&lt;/h3&gt;

&lt;p&gt;As a developer, I must admit, there's always the temptation to write functionality from &lt;a href="https://qr.ae/pNycBu"&gt;scratch&lt;/a&gt; instead of tweaking something that does a good job already. So I found a &lt;a href="https://github.com/tachyons-css/tachyons-sass"&gt;tachyons-scss&lt;/a&gt; library. It was perfect! Except, I still felt like I was hard-coding styles.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.b--dotted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dotted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.b--dashed&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dashed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.b--solid&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.b--none&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint-not-small&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.b--dotted-ns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dotted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.b--dashed-ns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dashed&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.b--solid-ns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;.b--none-ns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;   &lt;span class="nl"&gt;border-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;
  
  
  Iteration 1 - Preonize
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Generate a library
&lt;/h3&gt;

&lt;p&gt;What if I defined all my rules using sass maps like that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'blue'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#365e86&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'white'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#ffffff&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'grey'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#eef0ee&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'grey-dark'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#89969d&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then applied them to a css property like &lt;code&gt;background-color&lt;/code&gt; to get this:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.bg-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#365e86&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.bg-white&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.bg-grey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#eef0ee&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;.bg-grey-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#89969d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The icing on the cake is having each rule at different breakpoints in the right order. Priority problem solved.&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.bg-blue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#365e86&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;40em&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.bg-blue-m&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#365e86&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;@media&lt;/span&gt; &lt;span class="n"&gt;screen&lt;/span&gt; &lt;span class="n"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60em&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;.bg-blue-l&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#365e86&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 so I wrote the preonize function. And that was it. Almost.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;preonize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;value&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;@each&lt;/span&gt; &lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoint-value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint-value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nc"&gt;-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;value&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;All I had to do was define all my reusable rules, and apply them using &lt;code&gt;preonize&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;preonize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'bg-'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;background-color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$colors&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;preonize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fill-'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fill&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$colors&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;preonize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$colors&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// etc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Preonize took 4 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a class prefix like &lt;code&gt;bg-&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a css property like &lt;code&gt;background-color&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a sass map of rules like &lt;code&gt;$colors: (white: #ffffff, black: #000000)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;and an array of breakpoints to apply those rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I could generate an entire functional CSS library from a &lt;a href="https://github.com/gemmadlou/Preon/blob/master/assets/scss/preon.scss"&gt;base sass file&lt;/a&gt;. Minor changes were now trivial. Update the sass maps; compile to css.&lt;/p&gt;

&lt;p&gt;If I left it at this, I'd be happy. I already built &lt;a href="https://carolblackmusic.co.uk"&gt;carolblackmusic.co.uk&lt;/a&gt;, &lt;a href="https://pixelexaspect.com"&gt;pixelexaspect.com&lt;/a&gt; and &lt;a href="https://kammadata.com"&gt;kammadata.com&lt;/a&gt; this way. But I had come across some issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 3: Opinions and conventions
&lt;/h3&gt;

&lt;p&gt;There are definitely wrong ways to do things, but usually, there are no single right ways. But it's also true, that not having an opinion for a project will cause confusion eventually, even with one maintainer.&lt;/p&gt;

&lt;p&gt;So some of the classes I didn't like were the ones that redeclared rules for the same css property, like &lt;code&gt;padding-bottom&lt;/code&gt;. For example, padding-bottom could override a completely different class prefix &lt;code&gt;aspect-ratio&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This causes the priority issue again.&lt;/p&gt;

&lt;p&gt;I felt that there should be one class prefix for one css property.&lt;/p&gt;

&lt;p&gt;The other thing I didn't want was for a class to represent two CSS properties. One example is margins. So instead of one class representing &lt;code&gt;margin-left&lt;/code&gt; and &lt;code&gt;margin-right&lt;/code&gt; at the same time, it's more explicit to just use two.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- Instead of doing this --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"some.jpg"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hv-auto"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;

&lt;span class="c"&gt;&amp;lt;!-- I'd do this --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"some.jpg"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hl-auto hr-auto"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This decision would eventually help me to write simpler code generators for Preons, though, at the time, I didn't know it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 4 - Eat your own dog food
&lt;/h3&gt;

&lt;p&gt;I have &lt;a href="https://twitter.com/vicchi"&gt;Gary Gale&lt;/a&gt; to thank for the phrase. All it means is, what you're giving others to use, are you using it yourself?&lt;/p&gt;

&lt;p&gt;So it's all good building a CSS library but could &lt;em&gt;I&lt;/em&gt; use it? In fact, using it highlighted limitations of functional css in general very quickly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Displaying user generated articles&lt;/li&gt;
&lt;li&gt;Hovers and animations&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Limitation 1 - Displaying user-generated articles
&lt;/h3&gt;

&lt;p&gt;How could I expect a user to write an article and add margins to every paragraph?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;My wonderful article paragraph.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"fs1 fs2-m blue"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Next headline&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Doing so:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;would actually cause inconsistencies in the design&lt;/li&gt;
&lt;li&gt;make writing in WYSIWYGs difficult&lt;/li&gt;
&lt;li&gt;writing markdown would be no different from writing html&lt;/li&gt;
&lt;li&gt;just in general, the writing experience would be awful and take longer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the solution to this is to create scoped article classes such as &lt;code&gt;s-article&lt;/code&gt;, something I learned from working with &lt;a href="https://github.com/milad-alizadeh"&gt;Milad Alizadeh&lt;/a&gt; and &lt;a href="https://github.com/chrisboakes"&gt;Chris Boakes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here's a snippet I use for this Preons' documentation website:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nc"&gt;.s-article&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.lh0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.fs0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.lh1-m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nt"&gt;p&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.mb1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.fs2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.lh2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.mb2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.fwb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nt"&gt;h2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.fs1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.lh2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.pt2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.mb1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.fwb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.bwb1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.bsb-solid&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.bca-greyll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;@extend&lt;/span&gt; &lt;span class="nc"&gt;.lh4-m&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;Then I apply it where it is needed so it doesn't affect the entire site:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;class=&lt;/span&gt;&lt;span class="s"&gt;"s-article"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;nuxt-content&lt;/span&gt; &lt;span class="na"&gt;:document=&lt;/span&gt;&lt;span class="s"&gt;"page"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Limitation 2 - Hovering and animations
&lt;/h3&gt;

&lt;p&gt;Animations are the life-blood of interactive websites. At a minimum, we need to hover and change things like colours of text and backgrounds on buttons. Okay, it's not essential, but great for user experience. So a new preonize function would be needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@mixin&lt;/span&gt; &lt;span class="nf"&gt;preonize-hover&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;value&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;@each&lt;/span&gt; &lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$breakpoint-value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$breakpoints&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@media&lt;/span&gt; &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint-value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$value&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;.&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="si"&gt;}#{&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nc"&gt;-&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$breakpoint&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="nv"&gt;$prop&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="nd"&gt;:&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nt"&gt;value&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="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;
  
  
  Iteration 2 - A CLI
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Story
&lt;/h3&gt;

&lt;p&gt;After building a few sites, I had a problem. There were differences in the class conventions I used between them. What was &lt;code&gt;max-width&lt;/code&gt; again?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.mw1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/** or **/&lt;/span&gt;

&lt;span class="nc"&gt;.maxw1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&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 what about colours? What's the convention? The colour first, or the modifier first?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.dark-grey&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/** or **/&lt;/span&gt;

&lt;span class="nc"&gt;.grey-dark&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grey&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 had very few conventions and was mainly inspired by Tachyons. But switching between projects I was forgetting what rule applied where.&lt;/p&gt;

&lt;p&gt;Looking up classes wasn't easy using my sass file. I had to find the CSS class, then look up the corresponding global style.&lt;/p&gt;

&lt;p&gt;I could look through the CSS, but then I thought, that's not a great user experience. If I could config my rules in yaml or JSON, I could generate documentation.&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;
&lt;p&gt;Still my favorite &lt;a href="https://twitter.com/hashtag/dev?src=hash&amp;amp;ref_src=twsrc%5Etfw"&gt;#dev&lt;/a&gt; discovery of the year. Cyrille's concepts on living documentation. &lt;a href="https://t.co/psmtpI2czF"&gt;pic.twitter.com/psmtpI2czF&lt;/a&gt;&lt;/p&gt;— Gemma Black (@GemmaBlackUK) &lt;a href="https://twitter.com/GemmaBlackUK/status/1189968746800123905?ref_src=twsrc%5Etfw"&gt;October 31, 2019&lt;/a&gt;
&lt;/blockquote&gt;

&lt;p&gt;It was reading &lt;a href="https://twitter.com/cyriux?lang=en"&gt;Cyrille Martraire's&lt;/a&gt; book on &lt;a href="https://www.amazon.co.uk/gp/customer-reviews/R1M97XQ5OMYLU9/ref=cm_cr_dp_d_rvw_ttl?ie=UTF8&amp;amp;ASIN=0134689321"&gt;Living Documentation&lt;/a&gt; that cemented the idea of turning existing code into docs. And your code would always be in sync with your documentation. But it was a &lt;a href="https://youtu.be/j6ow-UemzBc?t=1190"&gt;Michael Bryzek video&lt;/a&gt; that took the concept one step further, generating code from your config.&lt;/p&gt;

&lt;p&gt;And he had a point.&lt;/p&gt;

&lt;p&gt;With a config, I could generate styles themselves from a yaml config into any CSS style language I liked.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sass&lt;/li&gt;
&lt;li&gt;Postcss&lt;/li&gt;
&lt;li&gt;Less.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One single config would be the definition for both the library &lt;em&gt;and&lt;/em&gt; the documentation.&lt;br&gt;
So &lt;code&gt;preons.yaml&lt;/code&gt; was born.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;preons&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;baseline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.2rem;&lt;/span&gt;

  &lt;span class="na"&gt;gutter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2.5rem;&lt;/span&gt;

  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# Preon breakpoints&lt;/span&gt;
  &lt;span class="na"&gt;breakpoints&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# Preon classes&lt;/span&gt;
  &lt;span class="na"&gt;classes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bg-&lt;/span&gt;
      &lt;span class="na"&gt;css-property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;background-color&lt;/span&gt;
      &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;color&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bg-&lt;/span&gt;
      &lt;span class="na"&gt;css-property&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;background-color&lt;/span&gt;
      &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;color&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then I stopped working on the project...&lt;/p&gt;

&lt;p&gt;For ages.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limitation 3 - Mixing and matching rules
&lt;/h3&gt;

&lt;p&gt;For widths, I wanted to use gradations of both percentages and &lt;code&gt;rems&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For margins, I wanted the same, but also negative rems. So unless I repeated myself, I needed a way of mixing different global rules together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$scaled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;n1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-1rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-2rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-3rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-4rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-5rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-6rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n7&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-7rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n8&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-8rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-9rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;n10&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-10rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;7rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;9&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;9rem&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10rem&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;So I found &lt;code&gt;map-collect&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@function&lt;/span&gt; &lt;span class="nf"&gt;map-collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$maps&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="nv"&gt;$collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;@each&lt;/span&gt; &lt;span class="nv"&gt;$map&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="nv"&gt;$maps&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$collection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;map-merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$collection&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$map&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="nv"&gt;$collection&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;Then I could mix-and-match them across different css properties:&lt;/p&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;preonize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'pa'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
  &lt;span class="n"&gt;padding&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
  &lt;span class="nv"&gt;$scaled&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
  &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;@include&lt;/span&gt; &lt;span class="nd"&gt;preonize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'pl'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;padding-left&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;map-collect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$scaled&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$discrete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
  &lt;span class="nv"&gt;$breakpoints&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Iteration 3 - Tweaking the config
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Array vs object of rules
&lt;/h3&gt;

&lt;p&gt;It occurred to me. Why am I using an array of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Properties_Reference"&gt;properties&lt;/a&gt; when it should just be an object? I can't redeclare the same property twice because of the priority problem and the convention to solve that problem of 1 css property to 1 class prefix.&lt;/p&gt;

&lt;p&gt;Yaml linters highlight when you have duplicate properties in an object. It is not allowed. So that's a win in applying the convention.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global vs standalone rules
&lt;/h3&gt;

&lt;p&gt;I also didn't like having to declare global rules for each CSS property. Sometimes rules only have to be applied once, eg. for display: flex&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;align-content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;content-&lt;/span&gt;
  &lt;span class="na"&gt;values&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flex-start&lt;/span&gt;
    &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flex-end&lt;/span&gt;
    &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;center&lt;/span&gt;
    &lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;space-between&lt;/span&gt;
    &lt;span class="na"&gt;around&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;space-around&lt;/span&gt;
    &lt;span class="na"&gt;stretch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stretch&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;vs colors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;theme-colors&lt;/span&gt;

&lt;span class="na"&gt;background-color&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bg-&lt;/span&gt;
  &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;theme-colors&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  Generating documentation
&lt;/h3&gt;

&lt;p&gt;Doing &lt;code&gt;preons config&lt;/code&gt; would spit out JSON based on the preons.yaml to make creating documentation easier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"border-top-color"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"class"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bct-"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rule"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"theme-colors"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"values"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"black"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#242027"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"white"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#fefeff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"greyll"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#f6f5f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"greyl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#beb9cc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"grey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#7d778e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"greyd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#47454c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transparent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"transparent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"hotpink"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ea2889"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"mappings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-black"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#242027"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-white"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#fefeff"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-greyll"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#f6f5f9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-greyl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#beb9cc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-grey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#7d778e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-greyd"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#47454c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-transparent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"transparent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bct-hotpink"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#ea2889"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I use this json to build the reference for the &lt;a href="https://dev.to/search"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 5 - Release fast and automate versions
&lt;/h3&gt;

&lt;p&gt;Whether you adhere to &lt;a href="https://github.com/jashkenas/backbone/issues/2888#issuecomment-29076249"&gt;romantic versioning&lt;/a&gt;, &lt;a href="http://sentimentalversioning.org/"&gt;sentimental versioning&lt;/a&gt; or &lt;a href="https://semver.org/"&gt;semantic versioning&lt;/a&gt;, managing versions manually is a bottleneck. Furthermore, it's like doing paperwork. But if it can be automated, it saves so much time.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why?
&lt;/h3&gt;

&lt;p&gt;Because moving fast allows fixes and features to be released quickly. The developer writing the code should know if their update is breaking existing functionality or not, whether they are adding a feature, a fix, or an improvement or not.&lt;/p&gt;

&lt;p&gt;So I used Intuit's Auto. Running &lt;code&gt;npm run release&lt;/code&gt; along with &lt;a href="https://gist.github.com/stephenparish/9941e89d80e2bc58a153#format-of-the-commit-message"&gt;Angular style commits&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;bumps npm&lt;/li&gt;
&lt;li&gt;tags the project&lt;/li&gt;
&lt;li&gt;makes a release in GitHub&lt;/li&gt;
&lt;li&gt;creates the changelog&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 6 - Distribute files easily
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://twitter.com/mjackson"&gt;Michael Jackson's&lt;/a&gt; &lt;a href="https://unpkg.com/"&gt;Unpkg&lt;/a&gt; allows you to access any file from an npm library at any version and it's free to use 🙏. It made creating the &lt;a href="https://repl.it/@gemmadlou/Cards"&gt;example repl&lt;/a&gt; easy.&lt;/p&gt;

&lt;p&gt;Here's a great intro to it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://kentcdodds.com/blog/unpkg-an-open-source-cdn-for-npm"&gt;https://kentcdodds.com/blog/unpkg-an-open-source-cdn-for-npm&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Lesson 7 - Keeping the cli and stylesheets core
&lt;/h3&gt;

&lt;p&gt;"I don't know any good reason to split my codebase between stylesheet and cli over multiple repositories". So I package them together. Maybe this is bad. But until it becomes a problem, I decided to not worry about it.&lt;/p&gt;

&lt;p&gt;It means, preons-theme 0.3.28 was released at the same time as the cli which is also 0.3.28, even if the cli didn't change.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 8 - Skip the tests
&lt;/h3&gt;

&lt;p&gt;Now as of May 2020, there aren't any tests. There are no linters either. Dangerous? Maybe. But normally I'd fuss over assuring my code passes all sorts of code quality bells and whistles, not just because I'm a &lt;a href="https://shields.io/"&gt;badge junkie&lt;/a&gt;. This time, I was determined that version 0.0.z would just work and be functional.&lt;/p&gt;

&lt;p&gt;I think &lt;a href="https://twitter.com/_ericelliott?lang=en"&gt;Eric Elliot&lt;/a&gt; says &lt;a href="https://twitter.com/_ericelliott/status/1187063655533334529"&gt;something&lt;/a&gt; like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Make it work&lt;/li&gt;
&lt;li&gt;Make it stable&lt;/li&gt;
&lt;li&gt;Make it fast&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even though, he starts with tests, making it work is a lot more archaic for me. Make it stable is where tests come in and solidifying them under the right design.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Coding faster: Make it work, then make it good” by Michael Parker &lt;a href="https://link.medium.com/SRPxvd76F6"&gt;https://link.medium.com/SRPxvd76F6&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://medium.com/@gerterasmus23/the-greatest-example-ever-of-mvp-and-iterative-incremental-development-41fd718ece0"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XZqnYodg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://preons.co/images/iterative-vs-incremental.jpeg" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If anything, this is the biggest shift in my thinking. Having gone through several iterations, I finally designed a version of Preons that I actually like.&lt;/p&gt;

&lt;p&gt;If I started with TDD, I think I'd feel too precious about changing anything because TDD sometimes eats into the design phase, rather than just being a development tool.&lt;/p&gt;

&lt;p&gt;I genuinely think design comes before TDD and static type analysis. That's why JavaScript is so powerful. You can iterate fast without worrying about correctness of code.&lt;/p&gt;

&lt;blockquote class="twitter-tweet"&gt;
&lt;p&gt;An MVP is a viable prototype, not a final product. Don't worry about perfection. The idea is to get fast feedback from users.&lt;/p&gt;— Eric Elliott (@_ericelliott) &lt;a href="https://twitter.com/_ericelliott/status/1262549798256881664?ref_src=twsrc%5Etfw"&gt;May 19, 2020&lt;/a&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Lesson 9 - Design first
&lt;/h3&gt;

&lt;p&gt;I saw a &lt;a href="https://www.youtube.com/watch?v=mNeXuCYiE0U&amp;amp;t=19s"&gt;video&lt;/a&gt; where &lt;a href="https://jamesclear.com/goals-systems"&gt;James Clear&lt;/a&gt; talked about two sets of photography students. One group had to come up with the perfect photo, one time. Another group had to take lots of photos and present their best one. The latter scored better apparently because applying their knowledge came from trail and error, learning what worked through practice versus theorizing what worked. That &lt;em&gt;is&lt;/em&gt; pragmatism. Practice over theory.&lt;/p&gt;

&lt;p&gt;My fear is, in our desire to follow best practice, we use tools that can hinder us at the wrong time. During the design phase, the learning phase, the prototyping phase, getting something to work is most important. Proving the possibility is most important.&lt;/p&gt;

&lt;p&gt;Now I've blitzed through the most basic of features, I have a choice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;to start firming up what I have with tests&lt;/li&gt;
&lt;li&gt;try a different design&lt;/li&gt;
&lt;li&gt;add more features&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  In conclusion
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed the article or at least learned that your open source project doesn't have to be right the first time around. By no means am I a decent writer or even correct about everything in this article. So please share feedback.&lt;/p&gt;

&lt;p&gt;If you're on Twitter, just &lt;a href="https://twitter.com/GemmaBlackUK"&gt;@GemmaBlackUK&lt;/a&gt; and tell me what I did wrong 😅.&lt;/p&gt;

</description>
      <category>functionalcss</category>
      <category>css</category>
      <category>preons</category>
      <category>tachyons</category>
    </item>
  </channel>
</rss>
