<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Gemma Black - Senior Software Engineer]]></title><description><![CDATA[Gemma Black - Senior Software Engineer]]></description><link>https://gemmablack.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 14 May 2026 16:49:22 GMT</lastBuildDate><atom:link href="https://gemmablack.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Understanding Sequelize's 'escape' function]]></title><description><![CDATA[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.

Sequelize is a very robust database ORM for...]]></description><link>https://gemmablack.dev/understanding-sequelizes-escape-function</link><guid isPermaLink="true">https://gemmablack.dev/understanding-sequelizes-escape-function</guid><category><![CDATA[Sequelize]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[SQL]]></category><category><![CDATA[owasp]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sun, 05 Jan 2025 09:21:41 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>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.</p>
</blockquote>
<p>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.</p>
<p>So when I was asked recently, why I had not used the escape function in <a target="_blank" href="https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#applying-where-clauses">Sequelize's where</a> 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.</p>
<p>To answer, first, let's explore how SQL injection attacks happen.</p>
<h2 id="heading-preparing-our-database">Preparing our database</h2>
<p>We want to prepare a database to test how a SQL injection could happen.</p>
<blockquote>
<p>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 😁.</p>
</blockquote>
<p>Create a new SQLite database.</p>
<pre><code class="lang-bash">sqlite3 database.sqlite
</code></pre>
<p>Create a basic users table.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TABLE</span> <span class="hljs-keyword">users</span> (
    <span class="hljs-keyword">id</span> <span class="hljs-built_in">INTEGER</span> PRIMARY <span class="hljs-keyword">KEY</span>,
    <span class="hljs-keyword">name</span> <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
    email <span class="hljs-built_in">TEXT</span> <span class="hljs-keyword">UNIQUE</span> <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>
);
</code></pre>
<p>Insert some users into the database.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">users</span> (<span class="hljs-keyword">name</span>, email) <span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'John Doe'</span>, <span class="hljs-string">'john@example.com'</span>);
<span class="hljs-keyword">INSERT</span> <span class="hljs-keyword">INTO</span> <span class="hljs-keyword">users</span> (<span class="hljs-keyword">name</span>, email) <span class="hljs-keyword">VALUES</span> (<span class="hljs-string">'Bruch Wayne'</span>, <span class="hljs-string">'bruce@example.com'</span>);
</code></pre>
<p>Select those users from the database.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span>;
</code></pre>
<p>Now you should see two users.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>name</td><td>email</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>John Doe</td><td><a target="_blank" href="mailto:john@example.com">john@example.com</a></td></tr>
<tr>
<td>2</td><td>Bruch Wayne</td><td><a target="_blank" href="mailto:bruce@example.com">bruce@example.com</a></td></tr>
</tbody>
</table>
</div><h2 id="heading-selecting-a-single-user">Selecting a single user</h2>
<p>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.</p>
<p>Query a user by their id.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">where</span> <span class="hljs-keyword">id</span> = <span class="hljs-number">1</span>;
</code></pre>
<p>Now we only see one user.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>name</td><td>email</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>John Doe</td><td><a target="_blank" href="mailto:john@example.com">john@example.com</a></td></tr>
</tbody>
</table>
</div><h2 id="heading-attempting-a-sql-injection-attack">Attempting a SQL injection attack</h2>
<p>But what if we pretend to we have a SQL injection attack? Add <code>or '1' = '1'</code> to the end of the SQL query, and now that the last statement is always true, all users are returned.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">SELECT</span> * <span class="hljs-keyword">FROM</span> <span class="hljs-keyword">users</span> <span class="hljs-keyword">where</span> <span class="hljs-keyword">id</span> = <span class="hljs-string">'1'</span> <span class="hljs-keyword">or</span> <span class="hljs-string">'1'</span> = <span class="hljs-string">'1'</span>;
</code></pre>
<div class="hn-table">
<table>
<thead>
<tr>
<td>id</td><td>name</td><td>email</td></tr>
</thead>
<tbody>
<tr>
<td>1</td><td>John Doe</td><td><a target="_blank" href="mailto:john@example.com">john@example.com</a></td></tr>
<tr>
<td>2</td><td>Bruch Wayne</td><td><a target="_blank" href="mailto:bruce@example.com">bruce@example.com</a></td></tr>
</tbody>
</table>
</div><p>Users shouldn't have direct access to our database, so how would this disastrous situation happen in reality.</p>
<h2 id="heading-creating-a-nodejs-app-as-an-example">Creating a Node.js app as an example</h2>
<p>Initalise npm in a new folder.</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>Install Sequelize and its dependencies.</p>
<pre><code class="lang-bash">npm install sequelize sequelize-cli sqlite3
</code></pre>
<p>Create a basic index.js file which connects to the sqlite database (or your flavour of it).</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { Sequelize, DataTypes } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'sequelize'</span>);

<span class="hljs-keyword">const</span> sequelize = <span class="hljs-keyword">new</span> Sequelize({
    <span class="hljs-attr">dialect</span>: <span class="hljs-string">'sqlite'</span>,
    <span class="hljs-attr">storage</span>: <span class="hljs-string">'./database.sqlite'</span>,
});

(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-comment">// test the connection is Ok</span>
        <span class="hljs-keyword">await</span> sequelize.authenticate();

        <span class="hljs-comment">// Get the argument from the input</span>
        <span class="hljs-keyword">const</span> id = process.argv[<span class="hljs-number">2</span>];

        <span class="hljs-keyword">const</span> [results] = <span class="hljs-keyword">await</span> sequelize.query(<span class="hljs-string">`SELECT * FROM Users WHERE id = <span class="hljs-subst">${id}</span>`</span>);

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Raw query results:'</span>, results);

    } <span class="hljs-keyword">catch</span> (error) {

        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Unable to connect to the database:'</span>, error);

    } <span class="hljs-keyword">finally</span> {

        <span class="hljs-keyword">await</span> sequelize.close();

    }
})();
</code></pre>
<ul>
<li><p>It works by taking an argument from the input in the terminal.</p>
</li>
<li><p>It runs the query.</p>
</li>
<li><p>It outputs the results.</p>
</li>
</ul>
<h2 id="heading-running-a-safe-query-against-our-script">Running a safe query against our script</h2>
<p>Running the query produces the following output:</p>
<pre><code class="lang-bash">node index.js 1
</code></pre>
<pre><code class="lang-bash">Executing (default): SELECT 1+1 AS result
Executing (default): SELECT * FROM Users WHERE id = 1

Raw query results: [ { id: 1, name: <span class="hljs-string">'John Doe'</span>, email: <span class="hljs-string">'john@example.com'</span> } ]
</code></pre>
<p>That successfully returned the user.</p>
<h2 id="heading-running-a-sql-injection-attack-against-our-script">Running a SQL injection attack against our script</h2>
<p>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!</p>
<pre><code class="lang-bash">node index.js <span class="hljs-string">"1 or '1' = '1'"</span> 

Executing (default): SELECT 1+1 AS result
Executing (default): SELECT * FROM Users WHERE id = 1 or <span class="hljs-string">'1'</span> = <span class="hljs-string">'1'</span>

Raw query results: [
  { id: 1, name: <span class="hljs-string">'John Doe'</span>, email: <span class="hljs-string">'john@example.com'</span> },
  { id: 2, name: <span class="hljs-string">'Bruch Wayne'</span>, email: <span class="hljs-string">'bruce@example.com'</span> }
]
</code></pre>
<p>So how can we fix this?</p>
<h2 id="heading-first-attempt-trying-to-escape-user-input">First attempt trying to escape user input</h2>
<p>Sequelize wraps the query in quotes. So we'll do just that. Replace the <code>const id</code> line with this.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> id = <span class="hljs-string">"'"</span> + process.argv[<span class="hljs-number">2</span>] + <span class="hljs-string">"'"</span>;
</code></pre>
<p>And voila. That's fixed the injection...partly.</p>
<pre><code class="lang-bash">node 02_index.js <span class="hljs-string">"1 or 1=1"</span>

Executing (default): SELECT 1+1 AS result
Executing (default): SELECT * FROM Users WHERE id = <span class="hljs-string">'1 or 1=1'</span>

Raw query results: []
</code></pre>
<p>Selecting users by id with <code>1 or 1=1</code> doesn't match anything. So nothing is returned.</p>
<p>But what if we were to try and inject some apostrophes to end the first statement and add our own <code>OR</code> statement.</p>
<pre><code class="lang-javascript">node <span class="hljs-number">02</span>_index.js <span class="hljs-string">"1' or 1=1 or '"</span>

Executing (<span class="hljs-keyword">default</span>): SELECT <span class="hljs-number">1</span>+<span class="hljs-number">1</span> AS result
Executing (<span class="hljs-keyword">default</span>): SELECT * FROM Users WHERE id = <span class="hljs-string">'1'</span> or <span class="hljs-number">1</span>=<span class="hljs-number">1</span> or <span class="hljs-string">''</span>

Raw query results: [
  { <span class="hljs-attr">id</span>: <span class="hljs-number">1</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'John Doe'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'john@example.com'</span> },
  { <span class="hljs-attr">id</span>: <span class="hljs-number">2</span>, <span class="hljs-attr">name</span>: <span class="hljs-string">'Bruch Wayne'</span>, <span class="hljs-attr">email</span>: <span class="hljs-string">'bruce@example.com'</span> }
]
</code></pre>
<p>Now the disaster returns! We can get all users again!</p>
<h2 id="heading-escaping-user-inputted-quotes">Escaping user inputted quotes</h2>
<p>We need to escape the quotes as well, just like Sequelize does.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> id = <span class="hljs-string">"'"</span> + process.argv[<span class="hljs-number">2</span>].replace(<span class="hljs-regexp">/'/g</span>, <span class="hljs-string">"''"</span>) + <span class="hljs-string">"'"</span>;
</code></pre>
<pre><code class="lang-bash">node 03_index.js <span class="hljs-string">"1' or 1=1 or '"</span>

Executing (default): SELECT 1+1 AS result
Executing (default): SELECT * FROM Users WHERE id = <span class="hljs-string">'1'</span><span class="hljs-string">' or 1=1 or '</span><span class="hljs-string">''</span>

Raw query results: []
</code></pre>
<p>You can see how the whole paramater is a string in the last query.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h2kabtply3ycggvwu90b.png" alt="SQL queries" /></p>
<p>And this is what the Sequelize escape function does but is far more comprehensive, covering different sql dialects.</p>
<h2 id="heading-sequelize-escape-function">Sequelize escape function</h2>
<p>Instead of doing our own escaping, it's best to leave it to libraries that are battle-tested and proven.</p>
<p>It is safer just to do this.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> id = sequelize.escape(process.argv[<span class="hljs-number">2</span>]);
</code></pre>
<h2 id="heading-escaping-is-not-the-best-option-against-sql-injection-attacks">Escaping is not the best option against SQL injection attacks</h2>
<p>OWASP puts escaping user input as last on its list of acceptable ways of preventing sql injections. It's strongly discouraged!</p>
<p>Why?</p>
<p>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.</p>
<h2 id="heading-whats-better-than-escaping">What's better than escaping</h2>
<p>Using Sequelize's query builder is better than escaping manually.</p>
<p>As an example, create a user model.</p>
<pre><code class="lang-bash">npx sequelize-cli model:generate --name User --attributes name:string,email:string
</code></pre>
<p>Add the user model to the main script and replace the raw query with the model-based one.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> { Sequelize, DataTypes } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'sequelize'</span>);

<span class="hljs-keyword">const</span> sequelize = <span class="hljs-keyword">new</span> Sequelize({
    <span class="hljs-attr">dialect</span>: <span class="hljs-string">'sqlite'</span>,
    <span class="hljs-attr">storage</span>: <span class="hljs-string">'./database.sqlite'</span>,
});

<span class="hljs-keyword">const</span> User = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./models'</span>).User;

(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">await</span> sequelize.authenticate();

        <span class="hljs-keyword">const</span> [results] = <span class="hljs-keyword">await</span> User.findAll({ 
            <span class="hljs-attr">where</span>: {
                <span class="hljs-attr">id</span>: process.argv[<span class="hljs-number">2</span>]
            }
        });

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Raw query results:'</span>, results);

    } <span class="hljs-keyword">catch</span> (error) {

        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Unable to connect to the database:'</span>, error);

    } <span class="hljs-keyword">finally</span> {

        <span class="hljs-keyword">await</span> sequelize.close();

    }
})();
</code></pre>
<p>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.</p>
<pre><code class="lang-bash">node 05_index.js <span class="hljs-string">"1' or 1=1 or '"</span>

Executing (default): SELECT 1+1 AS result
Executing (default): SELECT `id`, `name`, `email` FROM `Users` AS `User` WHERE `User`.`id` = <span class="hljs-string">'1'</span><span class="hljs-string">' or 1=1 or '</span><span class="hljs-string">''</span>;

Raw query results: undefined
</code></pre>
<p>And what about if we use <a target="_blank" href="http://Op.like">Op.like</a> in a where statement?</p>
<blockquote>
<p>We would never do this on an ID but the principle is the same.</p>
</blockquote>
<pre><code class="lang-js">        <span class="hljs-keyword">const</span> [results] = <span class="hljs-keyword">await</span> User.findAll({ 
            <span class="hljs-attr">where</span>: {
                <span class="hljs-attr">id</span>: {
                    [Sequelize.Op.like]: <span class="hljs-string">'%'</span> + process.argv[<span class="hljs-number">2</span>] + <span class="hljs-string">'%'</span>
                }
            }
        });
</code></pre>
<p>The final query shows the entire statement is escaped.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l2xgz71znmoczet3wm32.png" alt="Different SQL queries" /></p>
<h2 id="heading-bind-parameter-better-than-escaping">Bind parameter. Better than escaping.</h2>
<p>Parameterised queries are great because we send the sql query separately from the data.</p>
<p>Note that was use a bind parameter, <code>'%:id%'</code>.</p>
<pre><code class="lang-js">        <span class="hljs-keyword">const</span> [results] = <span class="hljs-keyword">await</span> User.findAll({ 
            <span class="hljs-attr">where</span>: {
                <span class="hljs-attr">id</span>: {
                    [Sequelize.Op.like]: <span class="hljs-string">'%:id%'</span>
                }
            }
        },
        {
            <span class="hljs-attr">bind</span>: { <span class="hljs-attr">id</span>: process.argv[<span class="hljs-number">2</span>]}
        });
</code></pre>
<p>Notice what is sent to the database.</p>
<pre><code class="lang-bash">Executing (default): SELECT `id`, `name`, `email` FROM `Users` AS `User` WHERE `User`.`id` LIKE <span class="hljs-string">'%:id%'</span>;

Raw query results: undefined
</code></pre>
<p>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.</p>
<p>Parameterised queries are the <a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">recommended way</a> (according to OWASP) of making queries of any kind with user input, in the database.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>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.</p>
<p>And thank you for reading. If I've made any mistakes, do let me know.</p>
<h2 id="heading-reading-material">Reading material</h2>
<ul>
<li><p><a target="_blank" href="https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html">https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html</a></p>
</li>
<li><p><a target="_blank" 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">https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/05-Testing_for_SQL_Injection</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Node.js: using fs.watch to re-import CommonJS modules.]]></title><description><![CDATA[Disclaimer. This is an investigation into what is possible. Not a fully-blown solution into how to reload modules without restarting the whole server.

In a previous article, I was investigating how to use require.cache to refresh a module by deletin...]]></description><link>https://gemmablack.dev/nodejs-using-fswatch-to-re-import-commonjs-modules</link><guid isPermaLink="true">https://gemmablack.dev/nodejs-using-fswatch-to-re-import-commonjs-modules</guid><category><![CDATA[fs.watch]]></category><category><![CDATA[require.cache]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[commonjs]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sun, 17 Nov 2024 19:13:53 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/hsPFuudRg5I/upload/ff8b54563ba64c8e931466c4862bf46b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><em>Disclaimer. This is an investigation into what is possible. Not a fully-blown solution into how to reload modules without restarting the whole server.</em></p>
</blockquote>
<p>In a <a target="_blank" href="https://gemmablack.dev/nodejs-refreshing-a-module-using-requirecache">previous article</a>, I was investigating how to use <code>require.cache</code> to refresh a module by deleting it and requiring it again. eg.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'./some-module'</span>)

<span class="hljs-comment">// use module</span>

<span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./some-module'</span>)]

<span class="hljs-comment">// use reloaded module</span>
</code></pre>
<p>However, on its own, I can’t really think of a good use case to delete a cached module like that. My aim was to attempt to reload a module without restarting the server – mainly to reduce the wait time. And it works! To a degree.</p>
<h2 id="heading-fswatchfile">fs.watchFile</h2>
<p>So I have a <code>child.js</code> module that doesn’t export anything.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// child.js</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hi'</span>, <span class="hljs-built_in">Date</span>.now())
</code></pre>
<p>I then have a <code>main.js</code> that imports the <code>child.js</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)

fs.watchFile(<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child.js'</span>), <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'refresh'</span>)
    <span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child'</span>)]
    <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
})
</code></pre>
<p>Running <code>node main.js</code> with <code>fs.watchFile</code> kept the server running on its own. So every time I made a change to the file, it would refresh the child module as I changed the logged output.</p>
<p>The only downside is that I noticed a little bit of lag when using <code>fs.watchFile</code>. It didn’t pick up all the changes. So I tried <code>fs.watch</code>.</p>
<h2 id="heading-using-fswatch-instead-of-fswatchfile">Using fs.watch instead of fs.watchFile</h2>
<p>The <code>child.js</code> file was the same. The <code>main.js</code> was the same with everything except <code>fs.watchFile</code> was replaced with <code>fs.watch</code>.</p>
<p>And the lag was gone. Changes were reflected instantaneously.</p>
<p>Now the reason for this is because <code>fs.watchFile</code> uses polling. And the Node docs recommend using <code>fs.watch</code> instead.</p>
<blockquote>
<p>Using <a target="_blank" href="http://fs.watch"><code>fs.watch</code></a><a target="_blank" href="https://nodejs.org/docs/latest/api/fs.html#fswatchfilename-options-listener"><code>()</code> is more e</a>fficient than <code>fs.watchFile</code> and <code>fs.unwatchFile</code>. <a target="_blank" href="http://fs.watch"><code>fs.watch</code></a> should be used instead of <code>fs.watchFile</code> and <code>fs.unwatchFile</code> when possible. - <a target="_blank" href="https://nodejs.org/docs/latest/api/fs.html#fswatchfilefilename-options-listener">https://nodejs.org/docs/latest/api/fs.html#fswatchfilefilename-options-listener</a></p>
<h5 id="heading-caveats"><strong>Caveats</strong></h5>
<p>The <a target="_blank" href="http://fs.watch"><code>fs.watch</code></a> API is not 100% consistent across platforms, and is unavailable in some situations.</p>
</blockquote>
<h2 id="heading-an-expressjs-server">An ExpressJS server</h2>
<p>So now, how could I potentially get module reloading when a file changes without restarting the entire server?</p>
<p>First. Here’s a very basic Express.js server.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// -- child.js</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    res.send(<span class="hljs-string">'Hello, World!'</span>);
}

<span class="hljs-comment">// -- main.js</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> handle = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>);

<span class="hljs-keyword">const</span> app = express();

app.get(<span class="hljs-string">'/'</span>, handle);

<span class="hljs-keyword">const</span> PORT = <span class="hljs-number">3000</span>;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<p>Now, when I run the server doing <code>node main.js</code>, and make a request to the root path, <code>http :3000</code> (using HTTPIE), I get my <strong>“Hello, World!”</strong> response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731867180899/5cc4249e-1ac2-4823-a9b1-7225d0ff3a6f.png" alt class="image--center mx-auto" /></p>
<p>But without Nodemon or some other way of watching the file, changes to the file won’t be reflected. So let’s change that.</p>
<ol>
<li><p>The <code>main.js</code> file has to be adjusted so that the <code>child.js</code> module is lazily-loaded when the root path is requested.</p>
</li>
<li><p>A file watcher was added to watch changes to the <code>child.js</code>.</p>
</li>
</ol>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);

fs.watch(<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child.js'</span>), <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child'</span>)]
    <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
})

<span class="hljs-keyword">const</span> app = express();

app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">...args</span>) =&gt;</span> {
    <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)(...args)
});

<span class="hljs-keyword">const</span> PORT = <span class="hljs-number">3000</span>;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<p>And voila 🎉!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1731869307507/0435dbcb-9695-4c3f-a91a-909dbbd17a2b.png" alt class="image--center mx-auto" /></p>
<p>I made a change to the file, it was reloaded without restarting the server!</p>
<p>And for development, this is fine. We can use a variable to have reloading in a development only environment running it like <code>MODULE_RELOAD=true node main.js</code> .</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
<span class="hljs-keyword">const</span> handle = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>);

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-keyword">if</span> (process.env.MODULE_RELOAD) {
    fs.watch(<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child.js'</span>), <span class="hljs-function">() =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Server reloaded'</span>)
        <span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child.js'</span>)]
        <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
    })

    app.get(<span class="hljs-string">'/'</span>, <span class="hljs-function">(<span class="hljs-params">...args</span>) =&gt;</span> {
        <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)(...args)
    });
} <span class="hljs-keyword">else</span> {
    app.get(<span class="hljs-string">'/'</span>, handle);
}

<span class="hljs-keyword">const</span> PORT = <span class="hljs-number">3000</span>;
app.listen(PORT, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Server is running on http://localhost:<span class="hljs-subst">${PORT}</span>`</span>);
});
</code></pre>
<p>Of course, loading one file route path isn’t all that helpful either. But, I did say this was an investigation 😉.</p>
]]></content:encoded></item><item><title><![CDATA[Node.js: refreshing a module using require.cache]]></title><description><![CDATA[This is purely in reference to CommonJS modules.

TLDR
Before I bore you with why I did this, refreshing a module can be done by deleting its reference in the require.cache object. Like so:
require('./some-module')

// use module

delete require.cach...]]></description><link>https://gemmablack.dev/nodejs-refreshing-a-module-using-requirecache</link><guid isPermaLink="true">https://gemmablack.dev/nodejs-refreshing-a-module-using-requirecache</guid><category><![CDATA[require.cache]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[Cache Invalidation]]></category><category><![CDATA[cache]]></category><category><![CDATA[HMR]]></category><category><![CDATA[commonjs]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sun, 17 Nov 2024 09:15:02 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>This is purely in reference to CommonJS modules.</p>
</blockquote>
<h2 id="heading-tldr">TLDR</h2>
<p>Before I bore you with why I did this, refreshing a module can be done by deleting its reference in the <code>require.cache</code> object. Like so:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">require</span>(<span class="hljs-string">'./some-module'</span>)

<span class="hljs-comment">// use module</span>

<span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./some-module'</span>)]

<span class="hljs-comment">// use reloaded module</span>
</code></pre>
<p>Simples. But there are caveats. But first.</p>
<h2 id="heading-why-was-i-interested-in-refreshing-node-modules">Why was I interested in refreshing node modules?</h2>
<p>If you have a small project, you can just use <a target="_blank" href="https://www.npmjs.com/package/nodemon">nodemon</a> or <code>node -—watch</code> to watch for file changes, but it restarts the whole application from scratch.</p>
<ol>
<li><p>You lose state.</p>
</li>
<li><p>You have to wait for the application to restart</p>
</li>
</ol>
<p>Now I wasn’t worried about state. That’s not a major problem for me. But application start up time was.</p>
<p>I could try to improve application start up time, or, I could reload only the files I work on. But before I could use <code>require.cache</code>, I needed to understand a few core principles about it first.</p>
<h2 id="heading-caching">Caching</h2>
<p>Probably, old news, but Node caches modules you require. It’s great for performance as the module only needs to be loaded once. Let’s take the following CommonJS example.</p>
<p>We call <code>console.log</code> in our <code>child.js</code> file.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// child.js</span>
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hi'</span>, <span class="hljs-built_in">Date</span>.now())
</code></pre>
<p>And let’s say we reference our <code>child.js</code> file in our <code>main.js</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.js</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
</code></pre>
<p>If we run <code>node main.js</code>, we get our <code>hi</code> log with the timestamp:</p>
<pre><code class="lang-bash">hi 1731770123765
</code></pre>
<p>Notice, we’ve required <code>child.js</code> twice, but it only gets ran once.</p>
<p>This is because the module is loaded into <code>require.cache</code>. And the cached version is used on the second require.</p>
<p>And what does the cache look like?</p>
<pre><code class="lang-bash">[Object: null prototype] {
  <span class="hljs-string">'/Users/username/Workspace/project/00_required/main.js'</span>: {
    id: <span class="hljs-string">'.'</span>,
    path: <span class="hljs-string">'/Users/username/Workspace/project/00_required'</span>,
    exports: {},
    filename: <span class="hljs-string">'/Users/username/Workspace/project/00_required/main.js'</span>,
    loaded: <span class="hljs-literal">false</span>,
    children: [ [Object] ],
    paths: [
      <span class="hljs-string">'/Users/username/Workspace/project/00_required/node_modules'</span>,
      <span class="hljs-string">'/Users/username/Workspace/project/node_modules'</span>,
      <span class="hljs-string">'/Users/username/Workspace/node_modules'</span>,
      <span class="hljs-string">'/Users/username/node_modules'</span>,
      <span class="hljs-string">'/Users/node_modules'</span>,
      <span class="hljs-string">'/node_modules'</span>
    ],
    [Symbol(kIsMainSymbol)]: <span class="hljs-literal">true</span>,
    [Symbol(kIsCachedByESMLoader)]: <span class="hljs-literal">false</span>,
    [Symbol(kIsExecuting)]: <span class="hljs-literal">true</span>
  },
  <span class="hljs-string">'/Users/username/Workspace/project/00_required/child.js'</span>: {
    id: <span class="hljs-string">'/Users/username/Workspace/project/00_required/child.js'</span>,
    path: <span class="hljs-string">'/Users/username/Workspace/project/00_required'</span>,
    exports: {},
    filename: <span class="hljs-string">'/Users/username/Workspace/project/00_required/child.js'</span>,
    loaded: <span class="hljs-literal">true</span>,
    children: [],
    paths: [
      <span class="hljs-string">'/Users/username/Workspace/project/00_required/node_modules'</span>,
      <span class="hljs-string">'/Users/username/Workspace/project/node_modules'</span>,
      <span class="hljs-string">'/Users/username/Workspace/node_modules'</span>,
      <span class="hljs-string">'/Users/username/node_modules'</span>,
      <span class="hljs-string">'/Users/node_modules'</span>,
      <span class="hljs-string">'/node_modules'</span>
    ],
    [Symbol(kIsMainSymbol)]: <span class="hljs-literal">false</span>,
    [Symbol(kIsCachedByESMLoader)]: <span class="hljs-literal">false</span>,
    [Symbol(kIsExecuting)]: <span class="hljs-literal">false</span>
  }
}
</code></pre>
<p>But what if we want to invalidate the cache for our scenario - reloading on the files that have changed? That way we get the <code>child.js</code> console to run twice.</p>
<p>Well, we can just delete it.</p>
<h2 id="heading-invalidating-the-cache">Invalidating the cache</h2>
<p>If we delete the cache before requiring the file again, we can get Node to re-load the file as it can’t find it in the cache.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.js</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)

<span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child'</span>)]

<span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
</code></pre>
<p>Then we can see our file has been reloaded and we get two console commands, with two different timestamps.</p>
<pre><code class="lang-bash">hi 1731749319540
hi 1731749319548
</code></pre>
<p>But can this help with only loading the files I need for development. Well, it depends.</p>
<h2 id="heading-but-what-are-the-caveats">But what are the caveats?</h2>
<p>Things get a little interesting when you export functions or variables from the child file and reference them in the parent file through a variable. What do I mean?</p>
<p>First, imagine <code>child.js</code> now exports a function.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// child.js</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'hi'</span>, <span class="hljs-built_in">Date</span>.now())
</code></pre>
<p>Second, imagine the <code>main.js</code> not only requires the function but assigns it to a constant. Well, you cannot use a constant again in the same blocked scope:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// main.js</span>
<span class="hljs-keyword">const</span> child = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
child()

<span class="hljs-keyword">delete</span> <span class="hljs-built_in">require</span>.cache[<span class="hljs-built_in">require</span>.resolve(<span class="hljs-string">'./child'</span>)]

child = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
child()
</code></pre>
<p>You get this error:</p>
<pre><code class="lang-javascript">hi <span class="hljs-number">1731772783273</span>
/Users/username/Workspace/project/<span class="hljs-number">08</span>_required/main.js:<span class="hljs-number">6</span>
child = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./child'</span>)
      ^

<span class="hljs-built_in">TypeError</span>: Assignment to constant variable.
</code></pre>
<p>You say, you can solve this by using <code>let</code> instead, but 1) not being able to use <code>const</code> isn’t a good solution if you’re using it for immutability and 2) even if you use <code>let</code>, you will need reassign the required variables. And I envisioned this getting rather messy.</p>
<h2 id="heading-but-jest-deletes-the-cache-without-any-problems">But Jest deletes the cache without any problems!</h2>
<p>Correct, but notice how they use it in their <a target="_blank" href="https://jestjs.io/docs/jest-object#jestresetmodules">docs</a>.</p>
<pre><code class="lang-javascript">beforeEach(<span class="hljs-function">() =&gt;</span> {
  jest.resetModules();
});

test(<span class="hljs-string">'works'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> sum = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../sum'</span>);
});

test(<span class="hljs-string">'works too'</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> sum = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../sum'</span>);
  <span class="hljs-comment">// sum is a different copy of the sum module from the previous test.</span>
});
</code></pre>
<p>Notice how the required module is being required inside a function. It’s being lazy-loaded. So instead of being required and cached on application start up, it’s only required when the function runs.</p>
<p>For testing this is okay, performance is not as critical although waiting forever for tests aren’t fun either, but :</p>
<ol>
<li><p>In production, initial requests will get I/O latency before the required modules are cached.</p>
</li>
<li><p>We’d need to use anonymous functions that could be refreshed to avoid figuring out how to reassign variables again.</p>
</li>
<li><p>It would require changing how we write code to require modules in a function which goes against the conventions of how Nodejs was designed to be written.</p>
</li>
</ol>
<h2 id="heading-so-is-lazy-loading-is-out-of-the-question">So is lazy-loading is out of the question?</h2>
<p>Well, you would have to require modules in functions like this.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// abc.js</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'abc2 module loaded at '</span>, <span class="hljs-built_in">Date</span>.now());
};

<span class="hljs-comment">// child.js</span>
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> abc = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./abc'</span>)
};
</code></pre>
<p>And someone <a target="_blank" href="https://nimblewebdeveloper.com/blog/hot-reload-nodejs-server">achieved this</a> in his ExpressJs development environment by being a little more clever about <strong><em>what</em></strong> exactly he lazy-loaded.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// server/index.js</span>

<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> port = <span class="hljs-built_in">parseInt</span>(process.env.PORT, <span class="hljs-number">10</span>) || <span class="hljs-number">3000</span>;

<span class="hljs-comment">// ...</span>

<span class="hljs-comment">// File watcher could go here</span>

<span class="hljs-comment">// ...</span>

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">//Hot reload!</span>
<span class="hljs-comment">//ALL server routes are in this module!</span>
app.use(<span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
   <span class="hljs-built_in">require</span>(<span class="hljs-string">"./app/router"</span>)(req, res, next);
});

<span class="hljs-comment">//...</span>

app.listen(port, <span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (err) <span class="hljs-keyword">throw</span> err;
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`&gt; Ready on http://localhost:<span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<p>Notice:</p>
<ol>
<li><p>The initial request would have a slower load time due to needing to load <code>require("./app/router")</code>. But subsequent requests would be cached!</p>
</li>
<li><p>And that he separated the router handling into its own file in an anonymous function.</p>
</li>
</ol>
<blockquote>
<p>Disclaimer: I haven’t tried his solution yet, I’ll leave that for another article.</p>
</blockquote>
<h2 id="heading-so-can-we-achieve-some-sort-of-hot-module-reloading-in-native-nodejs">So can we achieve some sort of hot-module-reloading in native Node.js</h2>
<p>It’s possible but it’s awkward to achieve fully based on what I’ve investigated so far. It can be done however partially. Hopefully I’ll share that article soon.</p>
]]></content:encoded></item><item><title><![CDATA[Laravel: Modularizing Little Pieces of Logic Through Local Composer Packages]]></title><description><![CDATA[“Premature optimisation is the root of all evil”.
But sometimes you know upfront that you can create a clean module, separate from the rest of the Laravel app. And I like Kelly Sutton’s mantra – optimize for deletability.
Why?

“By working with code,...]]></description><link>https://gemmablack.dev/laravel-modularizing-little-pieces-of-logic-through-local-composer-packages</link><guid isPermaLink="true">https://gemmablack.dev/laravel-modularizing-little-pieces-of-logic-through-local-composer-packages</guid><category><![CDATA[Laravel]]></category><category><![CDATA[composer]]></category><category><![CDATA[modularity]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sun, 10 Nov 2024 21:40:30 GMT</pubDate><content:encoded><![CDATA[<p>“Premature optimisation is the root of all evil”.</p>
<p>But sometimes you know upfront that you can create a clean module, separate from the rest of the Laravel app. And I like Kelly Sutton’s mantra – <a target="_blank" href="https://kellysutton.com/2017/05/29/deletability.html">optimize for deletability</a>.</p>
<p>Why?</p>
<blockquote>
<p>“By working with code, we see that modularity and deletability are closely related. Properly modularized code is easy to delete”. - Kelly Sutton</p>
</blockquote>
<p>If a feature is spread across multiple files throughout different parts of the code base, when it comes to updating and maintaining the code, it means looking around everywhere to do so. It’s inefficient. And if you plan on having the code for a long time, chances are, it <em>will</em> be changed.</p>
<p>So a simple solution to keep the new feature separate and modularised, is to create a new autoload configuration in the <code>composer.json</code> file eg.</p>
<pre><code class="lang-json">    <span class="hljs-string">"autoload"</span>: {
        <span class="hljs-attr">"psr-4"</span>: {
            <span class="hljs-attr">"App\\"</span>: <span class="hljs-string">"app/"</span>,
            <span class="hljs-attr">"Database\\Factories\\"</span>: <span class="hljs-string">"database/factories/"</span>,
            <span class="hljs-attr">"Database\\Seeders\\"</span>: <span class="hljs-string">"database/seeders/"</span>,
            <span class="hljs-attr">"YourLib\\"</span>: <span class="hljs-string">"lib/"</span>
        }
    },
</code></pre>
<p>And voila!</p>
<p>Add your your new feature there:</p>
<pre><code class="lang-bash">touch lib/Abc/Example.php
</code></pre>
<pre><code class="lang-php"><span class="hljs-meta">&lt;?php</span>

<span class="hljs-keyword">namespace</span> <span class="hljs-title">YourLib</span>\<span class="hljs-title">Abc</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Example</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">greet</span>(<span class="hljs-params"><span class="hljs-keyword">string</span> $name</span>): <span class="hljs-title">string</span>
    </span>{
        <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello, <span class="hljs-subst">$name</span>!"</span>;
    }
}
</code></pre>
<h2 id="heading-a-step-further">A step further</h2>
<p>You can treat it like its own Composer package.</p>
<pre><code class="lang-bash">mkdir -p lib/composer/example
<span class="hljs-built_in">cd</span> lib/composer/example
</code></pre>
<p>You can define its dependencies:</p>
<pre><code class="lang-bash">{
    <span class="hljs-string">"name"</span>: <span class="hljs-string">"your-namespace/example"</span>,
    <span class="hljs-string">"description"</span>: <span class="hljs-string">"An example package"</span>,
    <span class="hljs-string">"type"</span>: <span class="hljs-string">"library"</span>,
    <span class="hljs-string">"autoload"</span>: {
        <span class="hljs-string">"psr-4"</span>: {
            <span class="hljs-string">"YourNamespace\\Example\\"</span>: <span class="hljs-string">"src/"</span>
        }
    },
    <span class="hljs-string">"require"</span>: {
        <span class="hljs-string">"php"</span>: <span class="hljs-string">"^8.2"</span>
    }
}
</code></pre>
<p>Then you can add it to your main composer.json file as a local repository:</p>
<pre><code class="lang-bash"><span class="hljs-string">"repositories"</span>: [
    {
        <span class="hljs-string">"type"</span>: <span class="hljs-string">"path"</span>,
        <span class="hljs-string">"url"</span>: <span class="hljs-string">"lib/composer/*"</span>
    }
]
</code></pre>
<p>And finally reference it as if it was a separate package.</p>
<pre><code class="lang-bash"><span class="hljs-string">"require"</span>: {
    <span class="hljs-string">"your-namespace/example"</span>: <span class="hljs-string">"*@dev"</span>
}
</code></pre>
<p>You’ll need to run <code>composer update</code> otherwise your code won’t find it.</p>
<p>And when you’re done, you can put it into its own repository and install it like any other compose package.</p>
<h2 id="heading-downsides-of-creating-a-local-package">Downsides of creating a local package</h2>
<p>So going to all this effort to create a local package that you don’t publish won’t prevent you from hoisting in application code into your module, at which, you’re now polluting the separated module.</p>
<p>If you’re not going to truly separate the package out at some point, just keeping it as an autoload path is more than sufficient. But it suffers the same problem, that you need discipline to not use your main application code or other modules within it.</p>
]]></content:encoded></item><item><title><![CDATA[Sacrilege: Why I decided to use JSON in a relational database to manage visa checklists?]]></title><description><![CDATA[So the consensus is that JSON in a relational database is bad. Now, while I’m not a database expert by no stretch of the imagination, I will just say that there’s nothing wrong about it if used in the right way.
So my problem: modelling visa checklis...]]></description><link>https://gemmablack.dev/sacrilege-why-i-decided-to-use-json-in-relational-database-to-manage-visa-checklists</link><guid isPermaLink="true">https://gemmablack.dev/sacrilege-why-i-decided-to-use-json-in-relational-database-to-manage-visa-checklists</guid><category><![CDATA[json]]></category><category><![CDATA[SQL]]></category><category><![CDATA[modelling]]></category><category><![CDATA[Databases]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Thu, 19 Sep 2024 10:22:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/BUkU-VhzW-s/upload/49eb4e5cd8c32bcccb7123b2a1e3a9ed.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>So the consensus is that JSON in a relational database is bad. Now, while I’m not a database expert by no stretch of the imagination, I will just say that there’s nothing wrong about it if used in the right way.</p>
<h2 id="heading-so-my-problem-modelling-visa-checklists">So my problem: modelling visa checklists</h2>
<p>I built the frontend to look like this:</p>
<p><img src="https://lh7-rt.googleusercontent.com/docsz/AD_4nXeuob_7mZXdBqXm0rivtfWx0MqxY41XRB54p_CPbf8fHYJohJsLeWvWefn9AC97kONuOr3E_s3JKebCxGUcKjD83ohgsk6CZwi9bHi2gXXiZs9Am9nZTFjerLJjczvnuXDtDD_tuuRc6kUco7CMFeQeGRjv?key=2PRJ3Zt1zIoz1c_AAE_1VA" alt /></p>
<p>So users can:</p>
<ol>
<li><p>Create own checklists for visa documents.</p>
</li>
<li><p>They can add and remove documents required for the visa.</p>
</li>
<li><p>And then for each document, they can outline the steps needed for it to be completed.</p>
</li>
</ol>
<p>Thinking this through, in terms of tables, you’d need:</p>
<ol>
<li><p>A table for the visa itself.</p>
</li>
<li><p>A table for the documents.</p>
</li>
<li><p>Then a table for the steps.</p>
</li>
<li><p>And potentially a pivot table if I wanted to really normalise things.</p>
</li>
</ol>
<p>Now three or four tables is nothing to complain about. But I was starting to question whether it’s worth modelling a very simple feature this way in the database.</p>
<ul>
<li><p>But then, if users can add steps and remove them at will, and add documents and remove them at will, now, <strong>imagine the database queries to do that</strong>.</p>
</li>
<li><p><strong>We’d need a transaction</strong> to first add or remove documents from the table, and then add or remove documents from the steps, especially if multiple people can edit that.</p>
</li>
<li><p>And while I’m definitely not an expert, <strong>hard deleting from a database</strong> has to be treated carefully so as to not <strong>cause gap locks</strong>. Gap locks then cause concurrency problems. Now we have to handle retries. It’s just a pain.</p>
</li>
</ul>
<h2 id="heading-realising-i-was-creating-configuration-data">Realising I was creating configuration data</h2>
<p>So, naturally, I thought, that’s a lot of work for the database to do for basically what is configuration. And there’s literally no benefit because I don’t any indices on configuration data that I’d need. I don’t need to normalise it. Yes, I’d be repeating data to some degree, but not enough for me to worry about space issues.</p>
<p>Even more so, when it came to the UI, it was super easy to just build the documents using pure Vue.js and that naturally created a JSON data structure.</p>
<p>I was able to send the JSON to the backend endpoint, validate the shape and required step types using PHP enums, and then simply save that data in the backend as is, pure JSON!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1726740276375/0bbd01fe-48cd-4329-b663-5105a1515f97.png" alt class="image--center mx-auto" /></p>
<p>And then, sending the JSON object back to the front end meant that Vue.js, simply hydrated the component that managed the visa documents with zero fuss. No translation or restructuring of data was needed between the front end or the backend. The backend saved JSON. The front end hydrated it. Voila!</p>
<h2 id="heading-what-if-indices-become-a-problem">What if indices become a problem?</h2>
<p>Now, if indices were to somehow become a problem, there are ways around it.</p>
<p>For example, we can use <a target="_blank" href="https://planetscale.com/learn/courses/mysql-for-developers/schema/generated-columns">generated columns</a> in MySQL and thankfully in SQLite. I learnt that from Aaron Francis’ article on Planetscale. If you’re using Postgres, then apparently you can create indices more easily with <a target="_blank" href="https://scalegrid.io/blog/using-jsonb-in-postgresql-how-to-effectively-store-index-json-data-in-postgresql/">JSONB and Gin</a>.</p>
<p>So hopefully I’m forgiven for using JSON in relational SQL. And if you’re interested, you can find the functional for <a target="_blank" href="https://melimundo.com">visa documents on Melimundo.com</a> where the plan is to help people prepare for things like the <a target="_blank" href="https://melimundo.com/spain-digital-nomad-visa">Digital Nomad Visa in Spain</a>. It’s a work in progress but I’m happy about that functionality.</p>
<p>Thanks for reading!</p>
]]></content:encoded></item><item><title><![CDATA[Why I'm Building Financial Calculators with a Minimal Technical Stack]]></title><description><![CDATA[I love Laravel.
I love Nuxt probably just as much.
And Vue.js ❤️.
And client-side apps in general.
And Serverless. And Containers. And Kubernetes!
And the list goes on.
...so why go back to basics? Really, do I need all of that, for calculators?
Some...]]></description><link>https://gemmablack.dev/why-im-building-financial-calculators-with-a-minimal-technical-stack</link><guid isPermaLink="true">https://gemmablack.dev/why-im-building-financial-calculators-with-a-minimal-technical-stack</guid><category><![CDATA[frameworkless]]></category><category><![CDATA[framework]]></category><category><![CDATA[fintech software development]]></category><category><![CDATA[modularity]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Wed, 28 Aug 2024 08:39:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/vqpQjiTNQvw/upload/4f9f8b1531335e8986bbb8425f2f950e.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I love Laravel.</p>
<p>I love Nuxt probably just as much.</p>
<p>And Vue.js ❤️.</p>
<p>And client-side apps in general.</p>
<p>And Serverless. And Containers. And Kubernetes!</p>
<p>And the list goes on.</p>
<p>...so why go back to basics? Really, do I need all of that, for calculators?</p>
<h2 id="heading-some-reasons-for-a-minimal-stack">Some reasons for a minimal stack</h2>
<h3 id="heading-preventing-dependency-drift">Preventing dependency drift</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://x.com/flaviocopes/status/1750195640783581192">https://x.com/flaviocopes/status/1750195640783581192</a></div>
<p> </p>
<p>Reinventing the wheel is a great learning exercise. However, <strong>it's better to leverage dependencies</strong> so we have more time to work on our core business problems.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://x.com/dmokafa/status/1322504956054745089">https://x.com/dmokafa/status/1322504956054745089</a></div>
<p> </p>
<p>Now, saying that, frameworks usually come with a tonne of dependencies that we may not even need. Now that was the case for my project. I just didn't need all these dependencies.</p>
<p>Maintaining dependencies isn't noticeable when you're continually working on a code base. Every 30 minutes here and there seems negligible. But it adds up.</p>
<p>Usually, with dependency heavy projects, when I come back to a code base after some time, I have to inevitably run a build or install the packages afresh, at which point, something is broken. Now there are ways to try and mitigate this but it doesn't always work, like using version locks, or Nix for the OS.</p>
<h3 id="heading-focusing-on-logic">Focusing on logic</h3>
<p>It's enjoyable. Learning and becoming proficient at a framework is truly one of life's joys as a developer. But when time is limited and a new feature needs to be created, I had to decide. What do I want to challenge myself with? The framework? Or the logic?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724830615123/e85feaf5-a8af-4449-971a-aa9367449f60.png" alt="https://www.radicalsimpli.city/" class="image--center mx-auto" /></p>
<div data-node-type="callout">
<div data-node-type="callout-emoji">💡</div>
<div data-node-type="callout-text"><em>"If it takes an hour to figure out what’s going on, well, that’s an hour that wasn’t spent doing something else more useful and interesting. </em>On top of that, tech deals a lot of time with itself instead of delivering business value.<em>” - </em><a target="_blank" href="https://www.radicalsimpli.city/">https://www.radicalsimpli.city/</a> (image)</div>
</div>

<p>So for this particular project, most files are just <code>.mjs</code> files, ran with the Bun runtime.</p>
<p>Why? Financial calculators are just that. Calculators.</p>
<p>I had to write tests. Also, using <code>bun:test</code> instead of the usual test library, minimising dependencies again. Although the outputs with Vitest or Jest are a lot prettier.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724833149082/6f03c71e-6f03-4172-b83c-dda9b2f9370b.png" alt class="image--center mx-auto" /></p>
<p>So whether I do FIFO, LIFO or weighted average cost methods for calculating realised gains and losses, all I had to create were blackboxes of logic. No framework required. Just pure functionality.</p>
<h3 id="heading-building-quickly">Building quickly</h3>
<p>I love client side frameworks, especially as I started off a front end developer. But I found working with HTMX weirdly efficient as most of my logic lives on the server for this project. When I needed a tiny bit of interactivity and dynamic data, I didn't see any reason to reach for Vue.js or even React!</p>
<p>Now if I needed the power of a JavaScript framework, I would certainly have had fun building with those. Or if I was developing a project with mostly third-party APIs, then of course. But for this project, I had no reason for it.</p>
<h3 id="heading-being-a-solo-dev">Being a solo dev</h3>
<p>Now there's a downside to being a solo dev. I can build something that only I will ever understand. And so using frameworks would give me a standard way for doing things. Without a framework, the onus is then on me to make sure I structure and document the code in a way that's easy for my future self to understand as well as other developers and that is extra time and thought.</p>
<p>So to get around building a confusing code base, my simple gotos were:</p>
<ol>
<li><p>Modularising the code into small replaceable/deletable chunks that use the Unix philosophy. <em>And sorry about the swearing at the beginning of the video</em>. This might even mean <em>some</em> duplication.</p>
<p> %[https://www.youtube.com/watch?v=1FPsJ-if2RU] </p>
</li>
<li><p><strong>Documenting inputs and outputs that leveraged IntelliSense and type checks</strong>. That meant for me, writing code was super simple as IntelliSense would highlight what I'd need to write.</p>
</li>
<li><p><strong>Keeping app and infrastructure separate from core logic</strong>. This is a principle found in the hexagonal architecture, and the aim is to be able to replace the framework in future, which I've never had to do. But the main reason is it makes the core logic easy to test. If there's not much pure logic, there's no need to do this.</p>
</li>
<li><p>Writing contextual code. My calculators are not pure mathematical calculators. They're contextual to order objects in my project. I also don't worry about input validation for every function because I believe the boundaries between user input and the core functions should validate what comes in.</p>
</li>
</ol>
<h2 id="heading-now-am-i-against-frameworks-and-dependencies">Now am I against frameworks and dependencies?</h2>
<p>No. I'm building another project using Laravel Inertia which makes use of Vue.js, because the ecosystem is so rich, and I didn't want to re-invent the wheel. I knew I'd need to integrate subscriptions, notifications, websockets or at least, server sent events, background queues, migrations – the list goes on.</p>
<p>I'd be crazy to recreate a bad version of a framework myself when I could just leverage something out there already, even if that means running into dependency issues.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://x.com/arvidkahl/status/1727115836752621689">https://x.com/arvidkahl/status/1727115836752621689</a></div>
<p> </p>
<p>The only thing I'd try to do, is limit the number of dependencies I add on top of Laravel to make it easier to maintain.</p>
<h2 id="heading-and-whats-the-stack">And what's the stack?</h2>
<ul>
<li><p>Node.js</p>
</li>
<li><p>HTMX</p>
</li>
<li><p>Alpine.js</p>
</li>
</ul>
<p>Maybe some would say this more complicated. But given I didn't know HTMX and Alpine.js when I started the project,</p>
<h2 id="heading-so-the-financial-calculators">So the financial calculators</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724830986054/e80b24df-3d8b-456e-b542-338c2b8267b9.png" alt class="image--center mx-auto" /></p>
<p>So I've built a very basic free <a target="_blank" href="https://uposcar.com/tools/pnl-fifo-calculator">fifo calculator</a> for stocks and options as well as using other accounting methods. And literally, I didn't have to fight a framework or any dependency.</p>
<h2 id="heading-so-frameworkless-forever">So, frameworkless forever?</h2>
<p>While modern frameworks and tools offer immense power and convenience, they can also introduce unnecessary complexity, especially for projects with straightforward requirements. By returning to a minimal stack, we can focus on the core logic and deliver value without getting bogged down by dependencies. This has been a liberating project for me, and I hope you get to enjoy a project with minimal dependencies too. But 100% frameworkless isn't realistic either for all projects.</p>
]]></content:encoded></item><item><title><![CDATA[16 Principles for Tech-led Start-ups as a Software Engineer]]></title><description><![CDATA[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 ben...]]></description><link>https://gemmablack.dev/16-principles-for-tech-led-start-ups-as-a-software-engineer</link><guid isPermaLink="true">https://gemmablack.dev/16-principles-for-tech-led-start-ups-as-a-software-engineer</guid><category><![CDATA[Startups]]></category><category><![CDATA[focus]]></category><category><![CDATA[Productivity]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Mon, 29 Jul 2024 15:57:58 GMT</pubDate><content:encoded><![CDATA[<p>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.</p>
<p>Two goals will influence all the principles I share.</p>
<ol>
<li><p>Achieving speed.</p>
</li>
<li><p>Propelling growth.</p>
</li>
</ol>
<p>I’ll start with general principles, and then more technical ones as I go on.</p>
<h2 id="heading-focus">Focus</h2>
<p>Even if we're a one-person startup, our singular goal can get lost and forgotten as we put our heads down to work. Imagine how much more difficult it is to keep focused with a multi-person team.</p>
<p>If our goal is that first 100 customers, as developers, we might get shiny-eye syndrome. We might start worrying about scaling once we've reached 100 customers when we should continue to iterate on features.</p>
<p>Whether we're a one-person or a multi-person team, we have to pull in one direction. We can't make progress if we're 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.</p>
<p>And what things do we need to focus on? It depends on where we are:</p>
<ul>
<li><p>Getting the first customer. E.g. Do we need to get that first feature out the door? How quickly can we iterate?</p>
</li>
<li><p>Growing to <em>N</em> customers. E.g. Do we need more features? How quickly can we release more?</p>
</li>
<li><p>Preventing churn. E.g. Do we need to fix bugs and improve stability?</p>
</li>
</ul>
<h2 id="heading-essentialism">Essentialism</h2>
<p>"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.</p>
<p>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.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmujp6gfln5cnys1elub.png" alt="Image description" /></p>
<p>What does that mean for us?</p>
<p>If we're trying to reach product-market fit, we're probably trying different features that resonate with customers, building things fast.</p>
<ul>
<li><p>Should we worry about infrastructure?</p>
</li>
<li><p>Should we even be worrying about scaling?</p>
</li>
<li><p>Should we be worrying about the cleanest and most maintainable code?</p>
</li>
</ul>
<p>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.</p>
<h2 id="heading-release-small-release-often">Release small, release often</h2>
<p><strong>From a developer's perspective, large releases are downright terrifying.</strong></p>
<p>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.</p>
<p>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.</p>
<h2 id="heading-dont-waste-time-on-developer-problems">Don't waste time on developer problems</h2>
<p>I believe in hiring tools like hiring people.</p>
<p>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, whether paid or free. Using these tools is almost like hiring the person who built them or paying them but for much lower costs.</p>
<p>But you might think, why have a developer use tools when they should be able to build them?</p>
<p>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!</p>
<p>So for instance, instead of letting our developer build a payment gateway integration from scratch to collect payments, which apparently took <a target="_blank" href="https://www.reddit.com/r/EntrepreneurRideAlong/comments/182yj72/heres_how_stripe_a_50_billion_company_got_its/">Stripe 6 months</a>, we can set up a beautiful payment UI in a day or less using payment services that do it for us. The difference?</p>
<p>Cost using payment SaaS company = transaction costs + 1 developer day Cost using our own developer = 120 developer days + tonnes of maintenance time!</p>
<p>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?</p>
<p>Basically, don't reinvent the wheel, at least, not prematurely. Build things that are unique to the business.</p>
<h2 id="heading-hire-tools-to-avoid-wasting-time-on-maintenance">Hire tools to avoid wasting time on maintenance</h2>
<p>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.</p>
<p>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.</p>
<p>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?</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<h2 id="heading-monitoring-as-qa">Monitoring as QA</h2>
<p>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?</p>
<p>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 release. Besides, a QA Engineer would probably do a better job of testing our code anyway, especially with fresh eyes anyway.</p>
<h2 id="heading-write-down-your-principles">Write down your principles</h2>
<p>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.</p>
<p>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:</p>
<ul>
<li><p>Use a third-party tool with a free tier &gt; open-source &gt; roll out own.</p>
</li>
<li><p>Use existing tools unless one doesn't exist.</p>
</li>
<li><p>Use modular monolith over separate service.</p>
</li>
<li><p>Scale vertically before worrying about scaling horizontally.</p>
</li>
</ul>
<h2 id="heading-have-transparent-roadmaps">Have transparent roadmaps</h2>
<p>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.</p>
<p>Why? Two things:</p>
<ol>
<li><p>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?</p>
</li>
<li><p>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.</p>
</li>
</ol>
<p>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.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/arxhlmx6te4ltobrm2cw.png" alt="Image description" /></p>
<h2 id="heading-choose-what-we-can-be-productive-in">Choose what we can be productive in</h2>
<p>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?</p>
<p>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.</p>
<p>Now if we have all the time in the world, we can make the thing as complicated as we like: <a target="_blank" href="https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition">https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpriseEdition</a>.</p>
<h2 id="heading-make-it-work-make-it-right-maybe-make-it-fast">Make it work, make it right, maybe make it fast</h2>
<p>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.</p>
<p>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.</p>
<h2 id="heading-forget-ceremonies-and-the-processes">Forget ceremonies and the processes</h2>
<p>"Agile processes" are the rage, but one of the fundamental principles from the Founding Fathers of the Agile Manifesto themselves is optimise for <a target="_blank" href="https://agilemanifesto.org/">"individuals and interactions over processes</a> and tools".</p>
<p>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.</p>
<h2 id="heading-push-to-prod">Push to prod</h2>
<p>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.</p>
<p>"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.</p>
<p>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.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=z-ATZTUgaAo">https://www.youtube.com/watch?v=z-ATZTUgaAo</a></div>
<p> </p>
<p>Of course, we need to test our features before the push.</p>
<h2 id="heading-measure-your-product-usage">Measure your product usage</h2>
<p>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.</p>
<h2 id="heading-make-intellisense-your-friend">Make IntelliSense your friend</h2>
<p>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 <a target="_blank" href="http://Claude.ai">Claude.ai</a>, then I asked it to write me the glue code. And it did!</p>
<p>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!</p>
<h2 id="heading-document-your-tools">Document your tools</h2>
<p>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.</p>
<p>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.</p>
<h2 id="heading-finally-dont-stress">Finally, don't stress</h2>
<p>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.</p>
]]></content:encoded></item><item><title><![CDATA[Building the Beta version of the FIFO Profit Calculator]]></title><description><![CDATA[Website: https://uposcar.com/tools/pnl-fifo-calculator
Why I Built It
So there I was, escaping the rain to move to sunny Spain. Until I started preparing to move, I never had to worry about selling my shares and stocks because we have ISA accounts in...]]></description><link>https://gemmablack.dev/building-the-beta-version-of-the-fifo-profit-calculator</link><guid isPermaLink="true">https://gemmablack.dev/building-the-beta-version-of-the-fifo-profit-calculator</guid><category><![CDATA[calculator]]></category><category><![CDATA[FIFO]]></category><category><![CDATA[tools]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Fri, 26 Jul 2024 19:14:52 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1722021114682/bd37d292-3934-4b24-ab57-5623ce44abd1.png" alt class="image--center mx-auto" /></p>
<p>Website: <a target="_blank" href="https://uposcar.com/tools/pnl-fifo-calculator">https://uposcar.com/tools/pnl-fifo-calculator</a></p>
<h3 id="heading-why-i-built-it">Why I Built It</h3>
<p>So there I was, escaping the rain to move to sunny Spain. Until I started preparing to move, I never had to worry about selling my shares and stocks because we have ISA accounts in the UK. Any profit from shares, or even interest from savings, is protected from tax. And then you don't even have to worry about completing a self-assessment.</p>
<p>In Spain, however, even if you have an ISA account, when you profit from selling (or disposing of) your assets, you trigger a taxable event, and you've got to pay (unless you're under some exemption like the Beckham Law). On top of that, when working out how much tax you have to pay, you can't just use the same method as in the UK.</p>
<p>The UK uses a pooling method with weighted averages to calculate the cost basis when working out your gains. Spain, however, uses the FIFO (First-In-First-Out) method for calculating gains.</p>
<p>I wanted to track my PnL (profit and loss) so I could work out how much tax I needed to put aside whenever I sold any shares. I needed a really simple tool. So I built one!</p>
<h3 id="heading-limitations-of-the-tool">Limitations of the tool</h3>
<p>While it doesn't work out taxes, the tool is free and calculates the PnL for one stock at a time. If you've got a tonne of trades going across a lot of stocks, well, it's probably too simple for your needs. However, if people are interested, I'll consider developing that tool to have more features.</p>
<p>It's not mobile-friendly, yet, but I look forward to working on that soon.</p>
<p>Remember to always seek advice from a professional about taxes if in doubt.</p>
<p>Now you can find the tool here:</p>
<p><a target="_blank" href="https://uposcar.com/tools/pnl-fifo-calculator">https://uposcar.com/tools/pnl-fifo-calculator</a></p>
]]></content:encoded></item><item><title><![CDATA[How to dump and restore a Postgres DB with new table ownership]]></title><description><![CDATA[I've used MySQL for years. But recently, I found myself working with PostgreSQL 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.
So first...]]></description><link>https://gemmablack.dev/how-to-dump-and-restore-a-postgres-db-with-new-table-ownership</link><guid isPermaLink="true">https://gemmablack.dev/how-to-dump-and-restore-a-postgres-db-with-new-table-ownership</guid><category><![CDATA[MySQL]]></category><category><![CDATA[PostgreSQL]]></category><category><![CDATA[Backup]]></category><category><![CDATA[restore backup]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Thu, 14 Mar 2024 08:17:35 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/oyXis2kALVg/upload/fa9c7f5c689fa38c4a9edb8e48022dff.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I've used <a target="_blank" href="https://www.mysql.com/">MySQL</a> for years. But recently, I found myself working with <a target="_blank" href="https://www.postgresql.org/">PostgreSQL</a> 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.</p>
<p>So first, I'll do a quick overview and then share the explanations afterwards.</p>
<h2 id="heading-quick-overview">Quick Overview</h2>
<p>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.</p>
<h3 id="heading-requirements">Requirements</h3>
<p>You need the correct privileges to both <a target="_blank" href="https://www.postgresql.org/docs/current/backup-dump.html">dump</a> the database and recreate all the database objects.</p>
<h3 id="heading-set-the-user-credentials">Set the user credentials</h3>
<p>Instead of passing options to <code>pg_dump</code> and <code>psql</code>, we can also set up our access credentials as environment variables. There are more <a target="_blank" href="https://www.postgresql.org/docs/current/libpq-pgpass.html">secure alternatives</a> to handling database access secrets though.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> PGHOST=&lt;host&gt;
<span class="hljs-built_in">export</span> PGUSER=&lt;user&gt;
<span class="hljs-built_in">export</span> PGPASSWORD=&lt;password&gt;
</code></pre>
<h3 id="heading-dumping-the-data">Dumping the data</h3>
<p>Notice how we dump the data with the <code>--no-privileges</code> and <code>--no-owner</code>. Don't include this if you want to keep the ownership as is.</p>
<pre><code class="lang-bash">pg_dump \                                                                          
  --no-privileges \
  --no-owner \ 
  dbname &gt; dbname.sql
</code></pre>
<h3 id="heading-creating-the-new-database">Creating the new database</h3>
<p>Create a new database from a template.</p>
<pre><code class="lang-bash">psql \
    -c <span class="hljs-string">"CREATE DATABASE newdb TEMPLATE template0;"</span>
</code></pre>
<h3 id="heading-restoring-the-data">Restoring the data</h3>
<p>Notice I used <code>--single-transaction</code> to import the data. If you don't mind importing the data where there are some errors, remove that option.</p>
<pre><code class="lang-bash">
psql \
    --single-transaction \
    new_db &lt; dbname.sql
</code></pre>
<h3 id="heading-changing-table-ownership">Changing table ownership</h3>
<p>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 <code>ALTER</code> commands and then run that.</p>
<pre><code class="lang-bash">psql newdb
</code></pre>
<p>Then within the <code>psql</code> shell, we create the <code>ALTER</code> commands.</p>
<pre><code class="lang-bash"><span class="hljs-comment">## :psql &gt;&gt;</span>

select <span class="hljs-string">'ALTER TABLE '</span> || t.tablename || <span class="hljs-string">' OWNER TO new_owner;'</span> 
 from  pg_tables t
 <span class="hljs-built_in">where</span> t.tableowner != <span class="hljs-string">'rdsadmin'</span>;
</code></pre>
<p>It will print out something like the following, which you will need to copy-paste, and run within the shell again.</p>
<pre><code class="lang-bash"><span class="hljs-comment">## :psql &gt;&gt;</span>

ALTER TABLE groups OWNER TO new_owner;
ALTER TABLE images OWNER TO new_owner;
ALTER TABLE <span class="hljs-built_in">jobs</span> OWNER TO new_owner;
</code></pre>
<p>And that's it.</p>
<ul>
<li><p>We've dumped the database.</p>
</li>
<li><p>We've restored it.</p>
</li>
<li><p>And we've changed the table ownership to what we want.</p>
</li>
</ul>
<p>Now, I'll share a few things I found in the documentation.</p>
<h2 id="heading-notes-on-how-the-dump-and-restore-work">Notes on how the dump and restore work</h2>
<h3 id="heading-libpq">Libpq</h3>
<p>Libpq is the "C application programmer's interface to PostgreSQL". So I needed to install <a target="_blank" href="https://www.postgresql.org/docs/9.5/libpq.html">Libpq</a> 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.</p>
<h3 id="heading-table-locks">Table locks</h3>
<p>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:</p>
<pre><code class="lang-bash">mysqldump \
    --single-transaction \
    --skip-lock-tables \
    dbname &gt; dbname.sql
</code></pre>
<p>The <code>--single-transaction</code> tells the <code>mysqldump</code> 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 <code>--skip-lock-tables</code>. That way, we get a consistent data output and no table locks.</p>
<blockquote>
<p>↗️ Run mysqldump without locking the tables - <a target="_blank" href="https://mysqldump.guru/run-mysqldump-without-locking-the-tables.html">https://mysqldump.guru/run-mysqldump-without-locking-the-tables.html</a></p>
</blockquote>
<p>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.</p>
<blockquote>
<p>↗️ Chapter 9. Backup and Restore - <a target="_blank" href="https://www.postgresql.org/docs/7.2/backup.htm">https://www.postgresql.org/docs/7.2/backup.htm</a></p>
</blockquote>
<h3 id="heading-using-options-instead-of-environment-variables-for-connection-settings">Using options instead of environment variables for connection settings</h3>
<p>Similar to MySQL, we have <code>-h host</code>, <code>-p port</code> , but we have <code>-W password</code> for the prompt and a capital <code>U</code> for <code>-U user</code>.</p>
<h3 id="heading-removing-ownership">Removing ownership</h3>
<p>If you want the table ownership and privileges to remain the same, providing <code>--no-privileges</code> and <code>--no-owner</code> is not necessary. However, without this, you get the following SQL in your SQL dump:</p>
<pre><code class="lang-bash">CREATE SCHEMA abc;

ALTER SCHEMA abc OWNER TO existing_user;
</code></pre>
<h3 id="heading-database-templates">Database Templates</h3>
<p><a target="_blank" href="https://www.postgresql.org/docs/current/manage-ag-templatedbs.html">Database Templates</a> in Postgres is a foreign concept to me in MySQL. However, when we do, <code>CREATE DATABASE</code> in Postgres, it's an alias for <code>CREATE DATABASE dbname TEMPLATE template1</code>.</p>
<p>Now, there are two templates by default. <code>template0</code> and <code>template1</code>. In the earlier example, I used <code>template0</code>, but why?</p>
<ul>
<li><p><code>template1</code> can be amended so you can create a new database with your customisations.</p>
</li>
<li><p><code>template0</code> doesn't have any customisations. So it's a pure, unadulterated database that you can clone and copy.</p>
</li>
</ul>
<h3 id="heading-all-or-nothing-imports">All-or-nothing imports</h3>
<p>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 <code>--single-transaction</code> on the import:</p>
<pre><code class="lang-bash">psql \
    --single-transaction \
    &lt; dumpfile.sql
</code></pre>
<blockquote>
<p>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." - <a target="_blank" href="https://www.postgresql.org/docs/current/backup-dump.html#:~:text=When%20using%20this%20mode%2C%20be,after%20a%20partially%20restored%20dump.">postgresql.org</a></p>
</blockquote>
<h2 id="heading-that-wasnt-so-bad">That wasn't so bad</h2>
<p>MySQL and Postgres are similar but they're <strong>not</strong> the same. Hopefully dumping and restoring in Postgres will be simpler (even if it's just for me) in future.</p>
]]></content:encoded></item><item><title><![CDATA[Hledger: Always Strict Mode]]></title><description><![CDATA[For me, Hledger makes double-entry accounting simple. In comparison to other fully-fledged do-it-all applications, being able to type into a plain text file and generate reports, a GUI, balance sheets and income statements is powerful.
However, I've ...]]></description><link>https://gemmablack.dev/hledger-always-strict-mode</link><guid isPermaLink="true">https://gemmablack.dev/hledger-always-strict-mode</guid><category><![CDATA[hledger]]></category><category><![CDATA[pta]]></category><category><![CDATA[plain text accounting]]></category><category><![CDATA[accounting]]></category><category><![CDATA[BookKeeping]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Tue, 12 Mar 2024 11:23:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/N4fdQbMJ0nI/upload/437528f86c2e1f3c94321e457ca53a54.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>For me, <a target="_blank" href="https://hledger.org/index.html">Hledger</a> makes double-entry accounting simple. In comparison to other fully-fledged do-it-all applications, being able to type into a <a target="_blank" href="https://www.youtube.com/watch?v=WgV6M1LyfNY">plain text</a> file and generate <a target="_blank" href="https://hledger.org/cookbook.html#reporting">reports</a>, a <a target="_blank" href="https://hledger.org/1.32/hledger-web.html">GUI</a>, <a target="_blank" href="https://hledger.org/1.32/hledger.html#balance">balance sheets</a> and <a target="_blank" href="https://hledger.org/1.32/hledger.html#incomestatement">income statements</a> is powerful.</p>
<p>However, I've discovered Hledger in strict mode makes things easier.</p>
<h3 id="heading-catching-typos">Catching typos</h3>
<p>I can make lots of silly mistakes in plain text. For example, when I finally came to closing the accounts, I had inconsistencies due to typos in my accounts. It was simple enough to fix them, but imagine handling hundreds of transactions with these errors!</p>
<p>Entering correct information the first time is far easier than trying to fix things after the fact. Thankfully with strict mode, we can't have little accidents like this.</p>
<p>By adding <code>-s</code>, we can get Hledger to check our accounts for us. Here's an example of an error message when I made a typo under strict mode doing <code>hledger web -s</code>:</p>
<pre><code class="lang-typescript">hledger-web: <span class="hljs-built_in">Error</span>: main.journal:<span class="hljs-number">15</span>:
   | <span class="hljs-number">2023</span><span class="hljs-number">-09</span><span class="hljs-number">-02</span> Advertising
<span class="hljs-number">15</span> |     expense:advertising             <span class="hljs-number">10</span> GBP
   |     ^^^^^^^^^^^^^^^^^^^
   |     assets:bank                    <span class="hljs-number">-10</span> GBP

Strict account checking is enabled, and
account <span class="hljs-string">"expense:advertising"</span> has not been declared.
Consider adding an account directive. Examples:

account expense:advertising
account expense:advertising    ; <span class="hljs-keyword">type</span>:A  ; (L,E,R,X,C,V)
</code></pre>
<h2 id="heading-changing-account-order-in-web-mode">Changing account order in Web mode</h2>
<p>By default in Hledger, accounts in web mode are ordered as:</p>
<ol>
<li><p>Assets</p>
</li>
<li><p>Equity</p>
</li>
<li><p>Expense</p>
</li>
<li><p>Income</p>
</li>
<li><p>Liabilities</p>
</li>
</ol>
<p>However, I noticed my expenses account was enormous in comparison to all the other accounts. With strict mode, as we have to declare accounts upfront, Hledger will use whatever order you declare.</p>
<p>So, I changed the order to this:</p>
<ol>
<li><p>Liabilities</p>
</li>
<li><p>Income</p>
</li>
<li><p>Equity</p>
</li>
<li><p>Assets</p>
</li>
<li><p>Expenses</p>
</li>
</ol>
<h2 id="heading-how-to-order-accounts-in-hledger">How to order accounts in Hledger</h2>
<p>You have to declare the top-level account eg. <code>account expenses</code> and not just <code>account expenses:manufacturing</code> before any transaction that uses them. I did it top-level accounts with each of their subaccount types together.</p>
<pre><code class="lang-typescript">; Liabilities
account liabilities 
account liabilities:loan:director

; Income
account income
account income:bank-coupon

; Equity
account equity
account equity:opening
account equity:share-capital

; Assets
account assets
account assets:bank
account assets:equipment

; Expenses
account expenses
account expenses:manufacturing

; Commodities
commodity <span class="hljs-number">1.00</span> GBP
</code></pre>
<p>But nothing is stopping you from grouping the top-level accounts first, and then all your sub-accounts afterwards.</p>
<pre><code class="lang-typescript">account liabilities
account income
account equity
account assets
account expenses

; Liabilities 
account liabilities:loan:director

; Income
account income:bank-coupon

...etc
</code></pre>
<h3 id="heading-managing-account-declarations-in-a-separate-file">Managing account declarations in a separate file</h3>
<p>If all of these accounts are too verbose for the main transactions file, you can move it out into a separate file and include it back. That keeps it nice and organised.</p>
<pre><code class="lang-typescript">include accounts.journal
</code></pre>
<h2 id="heading-always-keeping-strict-mode-on">Always keeping Strict Mode on</h2>
<p>If you have a new journal, adding strict mode will probably be a lot easier than having to retrofit it into an old journal. It might not be worth the effort. But for new journals, I've decided to always have strict mode by creating a bash alias. So that whenever I run Hledger, I catch silly issues upfront. As I use zsh, I added the alias to my zsh profile:</p>
<pre><code class="lang-typescript">echo <span class="hljs-string">'alias h="hledger -s"'</span> &gt;&gt; ~/.zshrc
</code></pre>
]]></content:encoded></item><item><title><![CDATA[NodeJS: 4.8x faster if we go back to callbacks!]]></title><description><![CDATA[Yeah, I said it!
Callbacks are 4.8x faster when running them parallel over async/await in parallel. And only 1.9x faster when we run sequential callbacks.

I've modified this article somewhat after I got some helpful and kind comments about my dodgy ...]]></description><link>https://gemmablack.dev/nodejs-347x-faster-if-we-go-back-to-callbacks</link><guid isPermaLink="true">https://gemmablack.dev/nodejs-347x-faster-if-we-go-back-to-callbacks</guid><category><![CDATA[Node.js]]></category><category><![CDATA[performance]]></category><category><![CDATA[golang]]></category><category><![CDATA[Callbacks and Promises]]></category><category><![CDATA[promises]]></category><category><![CDATA[async/await]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sat, 10 Feb 2024 09:51:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/NqOInJ-ttqM/upload/428484557bc4cbf5cb4923cad50bc7cf.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Yeah, I said it!</p>
<p>Callbacks are <strong>4.8x</strong> faster when running them parallel over async/await in parallel. And only <strong>1.9x</strong> faster when we run sequential callbacks.</p>
<blockquote>
<p>I've modified this article somewhat after I got some helpful and kind comments about my dodgy test. 😂🙏</p>
<p>Thank you to <a target="_blank" href="https://twitter.com/ricardoplopes">Ricardo Lopes</a> and <a target="_blank" href="https://github.com/baublet">Ryan Poe</a> 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.</p>
<p>So this is round 2 which addresses my initial errors. Previously, I said:</p>
<p><strong>NodeJS: 34.7x faster if we go back to callbacks! I shouldn't have believed it. 🤣</strong></p>
</blockquote>
<p>Not as impressive as my bad benchmarks before (and see comments for context), but still a sizeable difference.</p>
<h2 id="heading-so-what-exactly-did-i-test">So what exactly did I test?</h2>
<p>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.</p>
<p>Then I finally compared callbacks in Node.js to Go!</p>
<p>Now, guess who won?</p>
<p>I won't be mean. <strong>TLDR</strong>. <s>Node.js callbacks</s> Golang!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707581413134/afcda514-8da2-41e6-ae1b-d45b2e5e6bba.png" alt class="image--center mx-auto" /></p>
<blockquote>
<p>Lower is better. Results are in <code>ms</code>.</p>
<p>Now, true benchmarkers? Go easy on me on this one. But please do leave your comments to make me a better person.</p>
</blockquote>
<h2 id="heading-everyone-keeps-saying-that-nodejs-is-slow">Everyone keeps saying that Node.js is slow!</h2>
<p>And it bugs me out.</p>
<p>Because what does slow mean? As with all benchmarks, mine is contextual.</p>
<p>I started reading about the <a target="_blank" href="https://nodejs.org/en/guides/event-loop-timers-and-nexttick">Event Loop</a>, just to even begin to understand <a target="_blank" href="https://www.builder.io/blog/visual-guide-to-nodejs-event-loop">how it works</a>.</p>
<p>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 <a target="_blank" href="https://libuv.org/">pure C</a>. A number of threads could potentially handle these I/O operations. And that's where Node.js can shine, handling I/O.</p>
<p>Promises, however, get handled in the main, single executable thread. And async/await, is well, promises but now with blocking added.</p>
<p><a target="_blank" href="https://www.builder.io/blog/visual-guide-to-nodejs-event-loop"><img src="https://cdn.builder.io/api/v1/image/assets%2FYJIGb4i01jvw0SRdL5Bt%2F6b288555862049b4b5cd7f19e2ae909f?width=705" alt="Event loop consisting of 6 different queues." class="image--center mx-auto" /></a></p>
<h2 id="heading-so-are-callbacks-faster-than-promises">So are callbacks faster than promises?</h2>
<p>Let's put it to the test.</p>
<p>First off. My <em>machine</em>! Complements of working with <a target="_blank" href="https://kammadata.com/">Kamma</a>. It's important to note what resources we're working with. Plenty memory and CPU.</p>
<pre><code class="lang-bash">MacBook Pro (14-inch, 2021)
Chip      Apple M1 Pro
Memory    32 GB
Cores     10
NodeJS    v20.8.1
Go        1.21.0
</code></pre>
<p>So we have a <code>text.txt</code> file with an <em>original</em> message, <code>Hello, world</code>.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">echo</span> <span class="hljs-string">"Hello, world"</span> &gt; text.txt
</code></pre>
<p>And we'll read this text file using native Node.js, which means, zero node module dependencies because we don't want to drag node modules down with the heaviest objects in the universe.</p>
<p><img src="https://i.redd.it/tfugj4n3l6ez.png" alt="Heaviest Objects In The Universe : r/ProgrammerHumor" /></p>
<h3 id="heading-callbacks">Callbacks</h3>
<h4 id="heading-parallel-callbacks">Parallel callbacks</h4>
<p>First, let's start with <em>parallel</em> 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?</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; file-callback-parallel.test.mjs</span>
<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs"</span>;

test(<span class="hljs-string">'reading file 10,000 times with callback parallel'</span>, <span class="hljs-function">(<span class="hljs-params">t, done</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) {
        fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>}, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
            assert.strictEqual(data, <span class="hljs-string">"Hello, world"</span>);
            count++
            <span class="hljs-keyword">if</span> (count === <span class="hljs-number">10000</span>) {
                done()
            }
        })
    }
});
</code></pre>
<h4 id="heading-sequential-callbacks">Sequential callbacks</h4>
<p>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.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; file-callback-blocking.test.mjs</span>
<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs"</span>;

<span class="hljs-keyword">let</span> read = <span class="hljs-function">(<span class="hljs-params">i, callback</span>) =&gt;</span> {
    fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>}, <span class="hljs-function">(<span class="hljs-params">err, data</span>) =&gt;</span> {
        assert.strictEqual(data, <span class="hljs-string">"Hello, world"</span>);

        i += <span class="hljs-number">1</span>

        <span class="hljs-keyword">if</span> (i === <span class="hljs-number">10000</span>) {
            <span class="hljs-keyword">return</span> callback()
        }

        read(i, callback)
    })
}

test(<span class="hljs-string">'reading file 10,000 times with callback blocking'</span>, <span class="hljs-function">(<span class="hljs-params">t, done</span>) =&gt;</span> {
    read(<span class="hljs-number">0</span>, done)
});
</code></pre>
<h3 id="heading-asyncawait">Async/Await</h3>
<p>Then we have async/await. My favourite way of working with Nodejs.</p>
<h4 id="heading-parallel-asyncawait">Parallel async/await</h4>
<p>It's as parallel as I can get with async/await. I load all the <code>readFile</code> operations into an array and await them all using <code>Promise.all</code>.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; file-async-parallel.test.mjs</span>
<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs/promises"</span>;

test(<span class="hljs-string">'reading file 10,000 times with async parallel'</span>, <span class="hljs-keyword">async</span> (t) =&gt; {
    <span class="hljs-keyword">let</span> allFiles = []
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) {
        allFiles.push(fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>}))
    }

    <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(allFiles)
        .then(<span class="hljs-function"><span class="hljs-params">allFiles</span> =&gt;</span> {
            <span class="hljs-keyword">return</span> allFiles.forEach(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
                assert.strictEqual(data, <span class="hljs-string">"Hello, world"</span>);
            })
        })
});
</code></pre>
<h4 id="heading-sequential-asyncawait">Sequential Async/Await</h4>
<p>This was the easiest and most concise one to write.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; file-async-blocking.test.mjs</span>

<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs/promises"</span>;

test(<span class="hljs-string">'reading file 10,000 times with async blocking'</span>, <span class="hljs-keyword">async</span> (t) =&gt; {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) {
        <span class="hljs-keyword">let</span> data = <span class="hljs-keyword">await</span> fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>})
        assert.strictEqual(data, <span class="hljs-string">"Hello, world"</span>);
    }
});
</code></pre>
<h3 id="heading-promises">Promises</h3>
<p>Finally, we have promises without async/await. I've long stopped using them in favour of <code>async/await</code> but I was interested in whether they were performant or not.</p>
<h4 id="heading-parallel-promises">Parallel promises</h4>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; file-promise-parallel.test.mjs</span>
<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs/promises"</span>;

test(<span class="hljs-string">'reading file 10,000 times with promise parallel'</span>, <span class="hljs-function">(<span class="hljs-params">t, done</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> allFiles = []

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) {
        allFiles.push(fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>}))   
    }

    <span class="hljs-built_in">Promise</span>.all(allFiles)
        .then(<span class="hljs-function"><span class="hljs-params">allFiles</span> =&gt;</span> {
            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) {
                assert.strictEqual(allFiles[i], <span class="hljs-string">"Hello, world"</span>);
            }

            done()
        })
});
</code></pre>
<h4 id="heading-sequential-promises">Sequential promises.</h4>
<p>Again, we want to wait for the execution of all <code>readFile</code> operations.</p>
<blockquote>
<p>This doesn't seem quite right as it's technically still not waiting for each to finish.</p>
</blockquote>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; file-promise-blocking.test.mjs</span>
<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs/promises"</span>;

test(<span class="hljs-string">'reading file 10,000 times with promises blocking'</span>, <span class="hljs-function">(<span class="hljs-params">t, done</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) {
        <span class="hljs-keyword">let</span> data = fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>})
            .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
                assert.strictEqual(data, <span class="hljs-string">"Hello, world"</span>)
                count++
                <span class="hljs-keyword">if</span> (count === <span class="hljs-number">10000</span>) {
                    done()
                }
            })
    }
});
</code></pre>
<h2 id="heading-how-i-ran-the-tests">How I ran the tests</h2>
<p>And voila! Results 🎉! I even ran it a few times to get a better reading.</p>
<p>I ran each test by doing:</p>
<pre><code class="lang-bash">
node --<span class="hljs-built_in">test</span> &lt;file&gt;.mjs
</code></pre>
<p><strong>Reading a file 10,000 times with callbacks is over <s>34x</s> 5.8x faster than with async/await in parallel!</strong> It's also 4.7x faster than with promises in parallel!</p>
<p>So, in Node.js land, callbacks <em>are</em> more performant!</p>
<h2 id="heading-now-is-go-faster-than-nodejs">Now is Go faster than Node.js?</h2>
<p>Well, I don't write in Go, so this may be truly terrible code because I asked ChatGPT to help me and yet, it <em>seems</em> pretty decent.</p>
<p>Hey ho. Let's go. Our Golang code.</p>
<pre><code class="lang-go"><span class="hljs-keyword">package</span> main

<span class="hljs-keyword">import</span> (
    <span class="hljs-string">"fmt"</span>
    <span class="hljs-string">"io/ioutil"</span>
    <span class="hljs-string">"time"</span>
)

<span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {
    startTime := time.Now()

    <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++ {
        data, err := ioutil.ReadFile(<span class="hljs-string">"./text.txt"</span>)
        <span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {
            fmt.Printf(<span class="hljs-string">"Error reading file: %v\n"</span>, err)
            <span class="hljs-keyword">return</span>
        }
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">string</span>(data) != <span class="hljs-string">"Hello, world"</span> {
            fmt.Println(<span class="hljs-string">"File content mismatch: got"</span>, <span class="hljs-keyword">string</span>(data), <span class="hljs-string">", want Hello, world"</span>)
            <span class="hljs-keyword">return</span>
        }
    }

    duration := time.Since(startTime)
    fmt.Printf(<span class="hljs-string">"Test execution time: %v\n"</span>, duration)
}
</code></pre>
<p>And we run it as so:</p>
<pre><code class="lang-bash">go run main.go
</code></pre>
<p>And the results?</p>
<pre><code class="lang-bash">Test execution time: 58.877125ms
</code></pre>
<p>🤯 Go is 4.9x faster than Node.js using sequential callbacks. Node.js only comes close with parallel execution.</p>
<p>Node.js Async/await is 9.2x slower than Go.</p>
<p>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!</p>
<h2 id="heading-now-just-a-side-note-do-i-have-bad-benchmarks">Now just a side note. Do I have bad benchmarks?</h2>
<blockquote>
<p>I really did have terrible Benchmarks. Thank you again to Ricardo and Ryan.</p>
</blockquote>
<p>Yes, I did. Hopefully now they're better.</p>
<p>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.</p>
<p>I also don't know how many threads Node.js is using.</p>
<p>I don't know how my CPU cores affect Go vs Node.js performance.</p>
<p>I could just rent an AWS machine with one core and compare.</p>
<p>Is it because I'm on Mac M1?</p>
<p>How would Node.js perform on a Linux or...Windows? 😱</p>
<p>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.</p>
<h2 id="heading-now-do-you-really-want-to-use-callbacks">Now, do you really want to use callbacks?</h2>
<p>I mean, do you really, really want to?</p>
<p>I don't know. I definitely don't want to tell anyone what to do.</p>
<p>But I like the clean syntax of async/awaits.</p>
<p>They look better.</p>
<p>They read better.</p>
<p>I know better is subjective here but I remember callback-hell, and I was grateful when promises came into existence. It made Javascript bearable.</p>
<p>Now, <s>while Golang in this instance is slower</s> 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.</p>
<p>Anywho. This was fun. It was more of an exercise to help me understand how callbacks and I/O work in the Event Loop.</p>
<h2 id="heading-so-to-sign-out">So to sign out</h2>
<p>Is Node.js slow? Or are we just using Node.js on slow mode?</p>
<p>Probably where performance matters, Golang is worth the jump. I'll certainly be looking more at using Golang in future.</p>
<h2 id="heading-updates">Updates</h2>
<h3 id="heading-sequential-promises-1">Sequential promises</h3>
<p>If I rewrite it as below, I'm still using callbacks 😬 but at least it's sequential:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;
<span class="hljs-keyword">import</span> fs <span class="hljs-keyword">from</span> <span class="hljs-string">"node:fs/promises"</span>;

<span class="hljs-keyword">let</span> read = <span class="hljs-function">(<span class="hljs-params">i, callback</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> data = fs.readFile(<span class="hljs-string">"./text.txt"</span>, { <span class="hljs-attr">encoding</span>: <span class="hljs-string">'utf-8'</span>})
        .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
            assert.strictEqual(data, <span class="hljs-string">"Hello, world"</span>)
            i += <span class="hljs-number">1</span>

            <span class="hljs-keyword">if</span> (i === <span class="hljs-number">10000</span>) {
                <span class="hljs-keyword">return</span> callback()
            }

            read(i, callback)
        })
}

test(<span class="hljs-string">'reading file 10,000 times with promises blocking'</span>, <span class="hljs-function">(<span class="hljs-params">t, done</span>) =&gt;</span> {
    read(<span class="hljs-number">0</span>, done)
});
</code></pre>
<p>I didn't include the above in the benchmarks but it is slower and not surprisingly so at <code>532.777667ms</code>.</p>
]]></content:encoded></item><item><title><![CDATA[The Flexible and Relaxing Opposite to Time-Blocking]]></title><description><![CDATA[TLDR = todo-time + a todo list.
That's it.
Time blocking might work for you. That's great, genuinely. If it doesn't, here's my alternative.
Firstly, why do I care about this?
I'm going to deviate a little bit from talking about software engineering. ...]]></description><link>https://gemmablack.dev/the-flexible-and-relaxing-opposite-to-time-blocking</link><guid isPermaLink="true">https://gemmablack.dev/the-flexible-and-relaxing-opposite-to-time-blocking</guid><category><![CDATA[Productivity]]></category><category><![CDATA[Time management]]></category><category><![CDATA[To Do List]]></category><category><![CDATA[stress management]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Thu, 08 Feb 2024 17:32:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/zlABb6Gke24/upload/631db822f8b19e9472b5cb9bf6d55378.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>TLDR =</strong> todo-time + a todo list.</p>
<p>That's it.</p>
<p>Time blocking might work for you. That's great, genuinely. If it doesn't, here's my alternative.</p>
<h2 id="heading-firstly-why-do-i-care-about-this">Firstly, why do I care about this?</h2>
<p>I'm going to deviate a little bit from talking about software engineering. It's how I manage my time and get things done inside and outside of work. At least, I've been doing this since September 2023.</p>
<p>This may change, but for now, I'm railing against the idea that productivity requires time-blocking.</p>
<p>I also note, that my aim is to reduce stress while achieving my goals, and for the first time, I'm managing to inch towards things I've been dreaming about for years, like moving to another country, and doing so without trying to skimp on sleep and relaxation.</p>
<blockquote>
<p>I actively avoid time-blocking as much as I can.</p>
</blockquote>
<h2 id="heading-natural-time-blocks-are-necessary">Natural time-blocks are necessary</h2>
<p>While I try to avoid time-blocking unnecessarily, I'd be delusional to say that it's not needed. It's absolutely essential.</p>
<p>Time blocks are a natural part of life.</p>
<p>Anytime we need to be somewhere at the same time as someone else, that's a time block.</p>
<p>Think lectures at school.</p>
<p>Think about being at work, whether remote or in the office.</p>
<p>Think about synchronous meetings.</p>
<p>Think about weddings, funerals, the World Cup and meals with friends.</p>
<p>Think about sleep, even.</p>
<p>If you want to enjoy life in real-time with others; time block.</p>
<h2 id="heading-if-its-just-you-doing-work-by-yourself-why-time-block">If it's just you doing work, by yourself, why time block?</h2>
<p>Natural time blocks aren't bad. They're about congregating people around an event at a specific time.</p>
<p>The event starts. The event ends. The time is done.</p>
<p>But if it's just me working, what am I time-blocking for?</p>
<h2 id="heading-what-do-i-mean-by-the-time-blocking">What do I mean by the time blocking?</h2>
<p>So something else I need to clarify is what I mean by time blocking.</p>
<p>I don't just mean, setting a generic block of time in which to do anything. I mean, time-blocking to achieve a certain thing.</p>
<p>For example:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707407058390/5774c54e-3f4c-411a-86f5-d18897328e88.png" alt class="image--center mx-auto" /></p>
<p>Now this has a lot of things going for it:</p>
<ol>
<li><p>It looks very organised.</p>
</li>
<li><p>It's obvious what I'm supposed to be working on and when.</p>
</li>
<li><p>There is a clear structure to my day.</p>
</li>
</ol>
<p>But there are a few problems with this.</p>
<ul>
<li><p>Stress!</p>
</li>
<li><p>It assumes I can complete certain tasks by a certain time.</p>
</li>
<li><p>It assumes I won't be required at several points in the day to assist team members and customers with their queries.</p>
</li>
<li><p>It assumes my meeting will only last 30 minutes.</p>
</li>
<li><p>It assumes that I can't finish my task way within time and move on to something else.</p>
</li>
<li><p>It assumes I'm mentally and physically ready and optimised to work on those tasks at those times.</p>
</li>
</ul>
<p>So let me go into those problems a little more.</p>
<h2 id="heading-time-blocks-arent-flexible-enough-to-work-with-my-changes-in-energy">Time blocks aren't flexible enough to work with my changes in energy</h2>
<p>Time blocking just doesn't allow for the ebbs and flows of the day.</p>
<p>Everyone's day in many ways is the same. Yet, we're all different.</p>
<p>Sometimes I'm full of energy first thing. Other times I'm groggy. I might want a light and simple set of todos to get going. I might be ready to get my head down and tackle something hard that's been on my mind from the night before.</p>
<p>But time blocks are just too inflexible to take advantage of this natural ebb and flow of energy. It doesn't care if you're optimised to deliver 100% creative performance now. You have to wait for that time-block later.</p>
<h3 id="heading-an-example-from-mohammed-ali">An example from Mohammed Ali</h3>
<p>Now this seems like a deviation from time blocks, but it's a good reminder for me. When asked about how many push-ups he could do, see his answer below (or <a target="_blank" href="https://twitter.com/ArjunMahadevan/status/1690335964638466048">here</a> if the embed doesn't work):</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/ArjunMahadevan/status/1690335964638466048">https://twitter.com/ArjunMahadevan/status/1690335964638466048</a></div>
<p> </p>
<p>Notice, he only started counting the push-ups when they began to hurt.</p>
<p>It reminds me of non-personalised training advice. You see, some may say, do 2 minutes of running followed by 1 minute of walking, and repeat for 30 minutes and then increase running time until you get good at it. But what if that's too hard to start at that point? What if that's too easy? Who knows.</p>
<p>Another trainer instead, may check your heart rate has reached a certain maximum, then he'll make you walk until it you heart rate is steady at a certain level again. Your recovery rate might be 1 minute, it might be 15 minutes. But it's based on your energy levels and recovery rate, and not just blocks of time.</p>
<h2 id="heading-time-blocks-cause-unnecessary-stress">Time blocks cause unnecessary stress</h2>
<p>Deadlines cause stress.</p>
<p>If a deadline is from a customer, from investors, from the tax man, from our boss, fair enough, deadlines are inevitable. Stress is also good for growth. A little bit of stress during exercise makes us fitter and stronger. But stress isn't great for our health when it's chronic.</p>
<p>We've heard people say "I need to have a car by the time I'm 18." I need to be earning $1,000,000 by the time I'm 30. I need to own my house by the time I'm 35! Self-imposed deadlines cause stress. I need to fit into that dress before the wedding! It can be a great motivator for the big things.</p>
<p>But why increment unnecessary stress throughout your day, weeks and years for little tiny tasks you won't even remember tomorrow, minute by minute, and hour by hour?</p>
<p>If I've said, between 10:00-12:00, I do task A, then from 12:00-14:00, I do task B, what happens when it's 12:00, and I haven't finished task A? <strong>Stress!</strong></p>
<p>You could say, well, maybe just work faster.</p>
<p>But what does faster mean?</p>
<p>Do I need to? Is there a deadline? Do I want to do a shoddy job? Do I want to cut corners? Is it not important, so it doesn't matter if the quality is not good or not? Do I just deliver part of the work and be done with it?</p>
<p>Sometimes deadlines are important, but if the work needs to be done, and it needs to be done.</p>
<p>Time blocks add stress. Why not just <strong>not</strong> time block?</p>
<p>Other people say so too:</p>
<h2 id="heading-time-blocks-limit-creativity">Time blocks limit creativity</h2>
<p>For knowledge work, it limits our creativity. Not every piece of work needs a genius idea. A lot of work is a matter of just getting things done. But sometimes, you need that spark of imagination.</p>
<p>For example, maybe you've heard this quote:</p>
<blockquote>
<p>It's not that I'm so smart, it's just that I stay with problems longer. - Albert Einstein</p>
</blockquote>
<p>Time-blocking a task to get something done that you may not be able to achieve in that time, will limit creativity. Instead of trying to imagine a solution, I'll be scouring the internet for someone who has already solved my problem.</p>
<p>Unless the time-block is to come up with ideas, and not complete the work, time-blocks do not help me here.</p>
<p>Also, when my mind is fixated on a problem, I can't time-block my imagination. My brain just keeps going back to it. I've learned not to time-block that either.</p>
<h2 id="heading-im-a-software-engineer-and-estimating-is-really-hard">I'm a software engineer, and estimating is really hard.</h2>
<p>Take my example of having task A between 10:00-12:00 and task B between 12:00-14:00.</p>
<p>What happens if I finish task A by 10:05?</p>
<p>Do I do nothing for almost 2 hours? Do I start on task B instead? Then do I finish the day early? Maybe.</p>
<p>Again, time blocks require great estimates. Rarely is it easy to estimate so accurately about getting things done. Hopefully, our estimates aren't completely off but rarely are they accurate.</p>
<h2 id="heading-i-need-a-break-from-working-on-something-big">I need a break from working on something big</h2>
<p>And that big thing, even when broken down into smaller things can take days or longer. I can't just throw myself at it for days at a time. I want little breaks in between, and so smaller todos make for little productive breaks rather than stopping work completely.</p>
<p>For example, something simple, like when writing this article, I reached a point where I wanted a break. So I had a small to-do, which was to cross-post another article elsewhere. I also needed to go through my todos as a whole and reprioritise them. 20 minutes later, I was ready to start back on this again.</p>
<h2 id="heading-slowing-down-for-more-meaningful-work">Slowing down for more meaningful work</h2>
<blockquote>
<h4 id="heading-ecclesiastes-31-11-niv"><strong>Ecclesiastes 3:1-11 NIV</strong></h4>
<p>There is a time for everything, and a season for every activity under the heavens...He has made everything beautiful in its time.</p>
</blockquote>
<p>Time blocks don't care about the natural speed at which we do things.</p>
<p>But going slower, not so slow as to procrastinate and waste time, but slower to do something properly, not rushing, means having more pride in my work. If I rush, I do a bad job. We all do. Now there's a difference between been naturally fast, and rushing. Time blocks do not allow for that extra time to polish unless you time-block it in. I'd rather just do the thing, rather than having to plan to do the thing.</p>
<h2 id="heading-so-whats-my-alternative">So what's my alternative?</h2>
<h3 id="heading-a-i-need-to-know-how-much-available-time-i-have-for-todos">A. I need to know how much available time I have for todos.</h3>
<p>Here's an example of my Tuesday (which is my day off paid work where I volunteer instead). Anything that's not dead time (0.75 hours for transitioning between tasks) or isn't a non-negotiable activity eg. sleep, grooming or eating, gives me time for my Todos.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707412832831/f415b07b-ea5e-4e8c-9d63-ffcffc080637.png" alt class="image--center mx-auto" /></p>
<p>And that's it, at best, I'll have 2.5 hours for todos unless I'm happy to skimp on time spent elsewhere. If I plan a social, that will eat into my todo-time. If I decide to relax and just watch YouTube, that'll also eat into my todo-time.</p>
<p>But knowing I have 2.5 hours maximum of todo-time gives me a realistic view of what I can achieve and get done. Over a period of weeks and months, 2.5 hours every Tuesday will add up. But on its own, I'll only achieve so much.</p>
<h3 id="heading-b-having-a-list-of-todos">B. Having a list of todos</h3>
<p>Just having a list of todos, ideally categorised in some cognitively contextual way is more than all I need to be productive. For example, here are the highest priority todos on my list for today:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707412568071/db1396a5-c521-41e1-b5b0-387a8b327390.png" alt class="image--center mx-auto" /></p>
<p>If they're not important to me, they shouldn't be on my todo-list or at least, they shouldn't be high priority on my todo-list either.</p>
<h2 id="heading-c-prioritise-my-todo-list-based-on-my-goals">C. Prioritise my todo-list based on my goals</h2>
<p>My goals for the next year or so will determine what I spend my time on. It helps prioritise my todos so that I'm not just doing things for the sake of doing them, but I'm doing things that get me ever closer to my goal.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=XHRCTwvvGTU">https://www.youtube.com/watch?v=XHRCTwvvGTU</a></div>
<p> </p>
<p>Sometimes, I'm working on just one todo that spans multiple days. If the lack of progress seems to bother me, I'll break that todo down into smaller tasks. Some days, I'll whizz through a dozen little todos.</p>
<p>And that's it.</p>
<p>I don't know if that's simpler or not, but I feel less stressed working this way and more productive.</p>
]]></content:encoded></item><item><title><![CDATA[NodeJS: We can run tests natively!]]></title><description><![CDATA[Once upon a time, there was Mocha.
Then there was Jasmine. Or perhaps it was the other way around. But Mocha was first for me.
There was Jest.
Now there's Vitest.
If you want to write tests for your Nodejs app, you're spoilt for choice. You just run ...]]></description><link>https://gemmablack.dev/nodejs-we-can-run-tests-natively</link><guid isPermaLink="true">https://gemmablack.dev/nodejs-we-can-run-tests-natively</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Testing]]></category><category><![CDATA[Complexity]]></category><category><![CDATA[dependencies]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Tue, 06 Feb 2024 22:43:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1707260996829/12395ccc-1995-444e-aed1-afa58c43122b.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Once upon a time, there was Mocha.</p>
<p>Then there was Jasmine. Or perhaps it was the other way around. But Mocha was first for me.</p>
<p>There was Jest.</p>
<p>Now there's Vitest.</p>
<p>If you want to write tests for your Nodejs app, you're spoilt for choice. You just run <code>npm install</code> for the pleasure.</p>
<p>Some worked well with Grunt and Gulp.</p>
<p>Others worked great with Webpack.</p>
<p>Now there are those that work perfectly with Vite.</p>
<p>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!</p>
<p>So what does it look like? 💯 zero node modules required:</p>
<blockquote>
<p>And straight from the <a target="_blank" href="https://nodejs.org/docs/latest/api/test.html#test-runner">docs</a>.</p>
</blockquote>
<pre><code class="lang-javascript"><span class="hljs-comment">// &gt; node test.mjs</span>
<span class="hljs-keyword">import</span> test <span class="hljs-keyword">from</span> <span class="hljs-string">'node:test'</span>;
<span class="hljs-keyword">import</span> assert <span class="hljs-keyword">from</span> <span class="hljs-string">'node:assert'</span>;

test(<span class="hljs-string">'synchronous passing test'</span>, <span class="hljs-function">(<span class="hljs-params">t</span>) =&gt;</span> {
    assert.strictEqual(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
});
</code></pre>
<p>That's it!</p>
<p>Assertions? They're native.</p>
<p>Test runners? Also native.</p>
<p>The output? Delightful. 🤩</p>
<pre><code class="lang-javascript">✔ synchronous passing test (<span class="hljs-number">0.618667</span>ms)
ℹ tests <span class="hljs-number">1</span>
ℹ suites <span class="hljs-number">0</span>
ℹ pass <span class="hljs-number">1</span>
ℹ fail <span class="hljs-number">0</span>
ℹ cancelled <span class="hljs-number">0</span>
ℹ skipped <span class="hljs-number">0</span>
ℹ todo <span class="hljs-number">0</span>
ℹ duration_ms <span class="hljs-number">4.6255</span>
</code></pre>
<p>We can even run tests spanning multiple files. As long as the file ends with an acceptable glob pattern like <code>*.test.mjs</code> , you can run the following:</p>
<pre><code class="lang-bash">node --<span class="hljs-built_in">test</span>
</code></pre>
<blockquote>
<p>Default globs are:</p>
<ul>
<li><p><code>**/*.test.?(c|m)js</code></p>
</li>
<li><p><code>**/*-test.?(c|m)js</code></p>
</li>
<li><p><code>**/*_test.?(c|m)js</code></p>
</li>
<li><p><code>**/test-*.?(c|m)js</code></p>
</li>
<li><p><code>**/test.?(c|m)js</code></p>
</li>
<li><p><code>**/test/**/*.?(c|m)js</code></p>
</li>
</ul>
</blockquote>
<p>We even have <strong>watch mode</strong> for goodness' sake. How did I not know about this?</p>
<pre><code class="lang-bash">node --<span class="hljs-built_in">test</span> --watch
</code></pre>
<p>Code coverage? Yes. 😯</p>
<pre><code class="lang-bash"><span class="hljs-comment"># node --test --experimental-test-coverage</span>

ℹ tests 6
ℹ suites 2
ℹ pass 6
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 54.586833
ℹ start of coverage report
ℹ --------------------------------------------------------------
ℹ file          | line % | branch % | funcs % | uncovered lines
ℹ --------------------------------------------------------------
ℹ test.mjs      | 100.00 |   100.00 |  100.00 | 
ℹ test.test.mjs | 100.00 |   100.00 |  100.00 | 
ℹ --------------------------------------------------------------
ℹ all files     | 100.00 |   100.00 |  100.00 |
ℹ --------------------------------------------------------------
ℹ end of coverage report
</code></pre>
<p><code>describe/it</code> blocks? Yes, too! 🤯</p>
<pre><code class="lang-javascript">describe(<span class="hljs-string">'A thing'</span>, <span class="hljs-function">() =&gt;</span> {
    it(<span class="hljs-string">'should work'</span>, <span class="hljs-function">() =&gt;</span> {
        assert.strictEqual(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>);
    });

    it(<span class="hljs-string">'should be ok'</span>, <span class="hljs-function">() =&gt;</span> {
        assert.strictEqual(<span class="hljs-number">2</span>, <span class="hljs-number">2</span>);
    });

    describe(<span class="hljs-string">'a nested thing'</span>, <span class="hljs-function">() =&gt;</span> {
        it(<span class="hljs-string">'should work'</span>, <span class="hljs-function">() =&gt;</span> {
            assert.strictEqual(<span class="hljs-number">3</span>, <span class="hljs-number">3</span>);
        });
    });
});
</code></pre>
<p>And it looks beautiful may I add.</p>
<pre><code class="lang-javascript">▶ A thing
  ✔ should work (<span class="hljs-number">0.082667</span>ms)
  ✔ should be ok (<span class="hljs-number">0.051292</span>ms)
  ▶ a nested thing
    ✔ should work (<span class="hljs-number">0.05575</span>ms)
  ▶ a nested thing (<span class="hljs-number">0.126541</span>ms)

▶ A thing (<span class="hljs-number">0.512584</span>ms)
</code></pre>
<h2 id="heading-so-why-arent-we-using-more-of-nodejs-natively">So why aren't we using more of Nodejs natively?</h2>
<p>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.</p>
<h2 id="heading-reducing-our-dependencies-and-relying-on-native">Reducing our dependencies and relying on native</h2>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<p>Yet, <a target="_blank" href="https://twitter.com/ricardoplopes">Ricardo Lopes</a> proved you can have a sane zero-dependency mini <a target="_blank" href="https://rplopes.hashnode.dev/resist-exploding-complexity">web application</a> without even a <code>package.json</code> written in NodeJS:</p>
<blockquote>
<p>"There's a place for complex frameworks and architectures, sure. But for many projects, they may be an overkill."</p>
</blockquote>
<p>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 <code>node --test</code>?</p>
<p>I'll sign off with a <a target="_blank" href="https://x.com/flaviocopes?s=20">Flavio</a> Tweet in favour of reducing dependencies.</p>
<p><a target="_blank" href="https://twitter.com/flaviocopes/status/1754784031307084015?s=20">https://twitter.com/flaviocopes/status/1754784031307084015</a></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/flaviocopes/status/1754784031307084015">https://twitter.com/flaviocopes/status/1754784031307084015</a></div>
]]></content:encoded></item><item><title><![CDATA[NodeJS: Using console.dir() over console.log() with deeply nested objects.]]></title><description><![CDATA[console.dir() seems rather redundant. At least with console.log(), you can add as many arguments as you want, but with console.dir(), you can only log one.
Despite that, for debugging, I'll be using console.dir() more in future. Here's why?
Let's tak...]]></description><link>https://gemmablack.dev/nodejs-using-consoledir-over-consolelog-with-deeply-nested-objects</link><guid isPermaLink="true">https://gemmablack.dev/nodejs-using-consoledir-over-consolelog-with-deeply-nested-objects</guid><category><![CDATA[Node.js]]></category><category><![CDATA[debugging]]></category><category><![CDATA[console.log]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Tue, 06 Feb 2024 17:26:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/RjP3MmnomOw/upload/e056b74f4a44ab21b164091c7c430da9.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><code>console.dir()</code> seems rather redundant. At least with <code>console.log()</code>, you can add as many arguments as you want, but with <code>console.dir()</code>, you can only log one.</p>
<p>Despite that, for debugging, I'll be using <code>console.dir()</code> more in future. Here's why?</p>
<p>Let's take a deeply nested object.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> hello = {
    <span class="hljs-attr">world</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">nested</span>: {
        <span class="hljs-attr">obj</span>: {
            <span class="hljs-attr">with</span>: <span class="hljs-string">"some text"</span>,
            <span class="hljs-attr">and</span>: {
                <span class="hljs-attr">numbers</span>: {
                    <span class="hljs-attr">count</span>: [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">4</span>,<span class="hljs-number">5</span>,<span class="hljs-number">6</span>,<span class="hljs-number">7</span>,<span class="hljs-number">8</span>,<span class="hljs-number">9</span>,<span class="hljs-number">10</span>],
                    <span class="hljs-attr">symbol</span>: <span class="hljs-built_in">Symbol</span>(<span class="hljs-string">"1"</span>),
                }
            }
        }
    }
}
</code></pre>
<h3 id="heading-consolelog-to-debug">console.log() to debug</h3>
<p>Now, if you do a pure <code>console.log</code>:</p>
<ol>
<li><p>You'll be missing some of the properties that are past a certain depth.</p>
</li>
<li><p>The output is on a single line and not structured in an easily readable way.</p>
</li>
</ol>
<pre><code class="lang-bash"><span class="hljs-comment"># console.log(hello)</span>
{ world: <span class="hljs-literal">true</span>, nested: { obj: { with: <span class="hljs-string">'some text'</span>, and: [Object] } } }
</code></pre>
<h3 id="heading-consolelog-to-get-the-whole-object">console.log() to get the whole object</h3>
<p>How I'd normally get around expanding that <code>[Object]</code> is using trusty old <code>JSON.stringify</code>. And voila 🎉! We have the entire object displayed.</p>
<pre><code class="lang-bash"><span class="hljs-comment"># console.log(JSON.stringify(hello))</span>
{<span class="hljs-string">"world"</span>:<span class="hljs-literal">true</span>,<span class="hljs-string">"nested"</span>:{<span class="hljs-string">"obj"</span>:{<span class="hljs-string">"with"</span>:<span class="hljs-string">"some text"</span>,<span class="hljs-string">"and"</span>:{<span class="hljs-string">"numbers"</span>:{<span class="hljs-string">"count"</span>:[1,2,3,4,5,6,7,8,9,10]}}}}}
</code></pre>
<h3 id="heading-consolelog-but-pretty">console.log() but pretty</h3>
<p>While we could see the entire object before, it's not easy to read. We can fix that by using the <code>JSON.stringify</code> formatting argument:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># console.log(JSON.stringify(hello, null, 2))</span>
{
  <span class="hljs-string">"world"</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-string">"nested"</span>: {
    <span class="hljs-string">"obj"</span>: {
      <span class="hljs-string">"with"</span>: <span class="hljs-string">"some text"</span>,
      <span class="hljs-string">"and"</span>: {
        <span class="hljs-string">"numbers"</span>: {
          <span class="hljs-string">"count"</span>: [
            1,
            2,
            3,
            4,
            5,
            6,
            7,
            8,
            9,
            10
          ]
        }
      }
    }
  }
}
</code></pre>
<p>And this is much better, but it still isn't great. Look at that array! That's equally impractical.</p>
<h3 id="heading-consoledir-for-better-readability">console.dir() for better readability</h3>
<p>Now, compare all that hard work with <code>console.dir</code>. You have to add the depth you want to be printed to get the entire object out.</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">console</span>.dir(hello, { <span class="hljs-attr">depth</span>: <span class="hljs-number">99</span> })
</code></pre>
<p>And it looks like this:</p>
<pre><code class="lang-javascript">{
  <span class="hljs-attr">world</span>: <span class="hljs-literal">true</span>,
  <span class="hljs-attr">nested</span>: {
    <span class="hljs-attr">obj</span>: {
      <span class="hljs-attr">with</span>: <span class="hljs-string">'some text'</span>,
      <span class="hljs-attr">and</span>: {
        <span class="hljs-attr">numbers</span>: {
          <span class="hljs-attr">count</span>: [
            <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>,  <span class="hljs-number">5</span>,
            <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>
          ],
          <span class="hljs-attr">symbol</span>: <span class="hljs-built_in">Symbol</span>(<span class="hljs-number">1</span>)
        }
      }
    }
  }
}
</code></pre>
<p>It has nice terminal colours because it's not <code>JSON</code>. It's Javascript! It benefits from the conciseness of javascript and is more compact and yet more readable.</p>
<h3 id="heading-getting-a-little-more-out-consoledir">Getting a little more out console.dir()</h3>
<p>There are three options you can pass into the formatting argument with <code>console.dir()</code>. We've already covered <code>depth</code> so we'll skip that.</p>
<pre><code class="lang-javascript">
<span class="hljs-built_in">console</span>.dir(hello, {
    <span class="hljs-attr">showHidden</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">depth</span>: <span class="hljs-number">99</span>,
    <span class="hljs-attr">colors</span>: <span class="hljs-literal">true</span>
})
</code></pre>
<ul>
<li><code>showHidden</code> allows you to see Javascript annotations such as the length of an array.</li>
</ul>
<pre><code class="lang-javascript">{
  ...
        count: [ <span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>, <span class="hljs-number">5</span>, <span class="hljs-number">6</span>, <span class="hljs-number">7</span>, <span class="hljs-number">8</span>, <span class="hljs-number">9</span>, <span class="hljs-number">10</span>, [length]: <span class="hljs-number">10</span> ],
  ...
}
</code></pre>
<ul>
<li><code>colors</code> by default is <code>true</code> (for me) in the terminal, but you can turn it off if you want. I find the colouring helpful though:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707240290099/67a944d1-ea38-459b-b8d5-595c34a833d8.png" alt class="image--center mx-auto" /></p>
<p>There's nothing more to say really. As I'm reading the Nodejs and finding little gems to make life a little easier. Maybe it's helpful to you.</p>
]]></content:encoded></item><item><title><![CDATA["Read The Manual", he said.]]></title><description><![CDATA[After watching yet another Aaron Francis MySQL masterclass, I discovered a new function: PARTITION BY. What surprised me, is that it's not new.
PARTITION BY has been around since version MySQL 5.7! That's yonks ago!
I'll be the first to admit; that w...]]></description><link>https://gemmablack.dev/read-the-manual</link><guid isPermaLink="true">https://gemmablack.dev/read-the-manual</guid><category><![CDATA[MySQL]]></category><category><![CDATA[documentation]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Tue, 06 Feb 2024 01:40:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/Oaqk7qqNh_c/upload/ad06200a474034db3b8e4cc69f5d21c4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After watching yet another <a target="_blank" href="https://twitter.com/aarondfrancis">Aaron Francis</a> <a target="_blank" href="https://www.youtube.com/watch?v=ZdLd5Ijlv1o">MySQL masterclass</a>, I discovered a new function: <code>PARTITION BY</code>. What surprised me, is that it's not new.</p>
<p><code>PARTITION BY</code> has been around since version MySQL 5.7! That's yonks ago!</p>
<p>I'll be the first to admit; that while MySQL's documentation is really helpful at times, most of the time, it's like an ocean, full of information, and I just need a drop of it to solve my problem. So I find myself reaching for blog posts, forums and videos to solve my issue.</p>
<p>And that's been a problem for me; instead of becoming comfortable with the docs, I reach for Google.</p>
<p>So why did I get RT*Med at work? Partly, because my colleagues are kind enough not to curse. But mainly, we were trying to find a use case, hidden in our data, not knowing if we had it or not for QA purposes. The issue was that I had no idea how to even write the query.</p>
<h2 id="heading-what-use-case-was-i-looking-for">What use case was I looking for?</h2>
<p>Company IP aside, imagine we had a table with data that could be grouped by a non-unique key, <code>external_id</code>. I wanted to find a grouping where the last row grouped by date had a nullable field but the row dated before did not. eg. I want it to pick out grouping by <code>x</code> and <strong>not</strong> grouping by <code>y</code>.</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>external_id</td><td>Field A</td><td>Date</td></tr>
</thead>
<tbody>
<tr>
<td>x</td><td>Some data</td><td>2022-02-01</td></tr>
<tr>
<td>x</td><td>More data</td><td>2023-02-01</td></tr>
<tr>
<td>x</td><td>NULL</td><td>2024-02-01</td></tr>
<tr>
<td>y</td><td>Some data</td><td>2022-02-01</td></tr>
<tr>
<td>y</td><td>NULL</td><td>2023-02-01</td></tr>
<tr>
<td>y</td><td>Final data</td><td>2024-02-01</td></tr>
</tbody>
</table>
</div><p>Now of course, <code>GROUP BY</code> with <code>HAVING</code> came to mind immediately because that's what I know. But that would merely find rows with a random mixture of nullable and non-nullable columns.</p>
<p>My naive approach at best was:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">select</span>
    *,
    <span class="hljs-keyword">sum</span>(<span class="hljs-keyword">isnull</span>(field_a)) <span class="hljs-keyword">as</span> has_null,
    <span class="hljs-keyword">sum</span>(! <span class="hljs-keyword">isnull</span>(field_a)) <span class="hljs-keyword">as</span> has_not_null
<span class="hljs-keyword">from</span>
    my_table
<span class="hljs-keyword">group</span> <span class="hljs-keyword">by</span>
    external_id
<span class="hljs-keyword">having</span>
    <span class="hljs-keyword">sum</span>(<span class="hljs-keyword">isnull</span>(field_a)) = <span class="hljs-number">1</span>
    <span class="hljs-keyword">and</span> <span class="hljs-keyword">sum</span>(! <span class="hljs-keyword">isnull</span>(field_a)) &gt; <span class="hljs-number">0</span>;
</code></pre>
<p>This would have led to a useless answer:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707169830866/b98be0af-8c70-4900-9eba-8e4a57a9c11e.png" alt class="image--center mx-auto" /></p>
<p>A decade-plus of MySQL, shamefully, I didn't think this problem could be solved with MySQL alone. And yet, our database master (a.k.a, our CTO) solved the conundrum using it and his quick quip was (to paraphrase), "well, that's what happens when you RTM".</p>
<p>I managed to get this far as a developer while being squeamish about reading through documentation. It was his gain, and my loss in the end. I missed out on a fundamental piece of knowledge that would have helped create a simple MySQL query; MySQL <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html">Window Functions</a>.</p>
<h3 id="heading-discovering-window-function-in-mysql">Discovering Window Function in MySQL</h3>
<p>Now looking at MySQL's docs rather than Googling around, I found a surprisingly helpful <a target="_blank" href="https://dev.mysql.com/doc/refman/8.0/en/window-functions-usage.html">example</a> to copy. As I tend to learn by example, here's what MySQL shared:</p>
<pre><code class="lang-sql">       <span class="hljs-keyword">SELECT</span>
         <span class="hljs-keyword">year</span>, country, product, profit,
         <span class="hljs-keyword">SUM</span>(profit) <span class="hljs-keyword">OVER</span>() <span class="hljs-keyword">AS</span> total_profit,
         <span class="hljs-keyword">SUM</span>(profit) <span class="hljs-keyword">OVER</span>(<span class="hljs-keyword">PARTITION</span> <span class="hljs-keyword">BY</span> country) <span class="hljs-keyword">AS</span> country_profit
       <span class="hljs-keyword">FROM</span> sales
       <span class="hljs-keyword">ORDER</span> <span class="hljs-keyword">BY</span> country, <span class="hljs-keyword">year</span>, product, profit;
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707170306638/07039e6d-ef13-4678-8831-4f6315c1dd8c.png" alt class="image--center mx-auto" /></p>
<p>Now, I still don't fully understand how <code>OVER</code> works and it's a little hazy, but having a little think about things, and how to use <code>PARTITION BY</code>, I came up with this:</p>
<pre><code class="lang-sql"><span class="hljs-keyword">select</span>
    *
<span class="hljs-keyword">from</span>
    (
        <span class="hljs-keyword">select</span>
            *,
            <span class="hljs-comment">-- Get total in group without using GROUP BY</span>
            <span class="hljs-keyword">count</span>(external_id) <span class="hljs-keyword">over</span> (
                <span class="hljs-keyword">partition</span> <span class="hljs-keyword">by</span> external_id
            ) <span class="hljs-keyword">as</span> total_in_group,
            <span class="hljs-comment">-- Ordered by row number</span>
            row_number() <span class="hljs-keyword">over</span>(
                <span class="hljs-keyword">partition</span> <span class="hljs-keyword">by</span> external_id
                <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span>
                    external_id,
                    <span class="hljs-built_in">date</span>
            ) <span class="hljs-keyword">as</span> <span class="hljs-keyword">rownum</span>
        <span class="hljs-keyword">from</span>
            my_table
        <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span>
            external_id,
            <span class="hljs-keyword">rownum</span>
    ) t1
<span class="hljs-keyword">where</span> total_in_group = <span class="hljs-keyword">rownum</span>
<span class="hljs-keyword">and</span> field_a <span class="hljs-keyword">is</span> <span class="hljs-literal">null</span>
<span class="hljs-keyword">and</span> total_in_group &gt; <span class="hljs-number">1</span>;
</code></pre>
<p>Okay, so it looks a bit much, and there's probably a simpler way, but for a first pass, I'm pretty happy. And it was put together with some trial and error. So let's break it down a little.</p>
<p>For repetition, we have this data:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707171354982/aa3d9167-7d0b-40f9-9c29-10be79256f11.png" alt class="image--center mx-auto" /></p>
<p><code>field_a</code> is sometimes <code>null</code>, which is important to us.</p>
<h3 id="heading-counting-groups-of-data-without-group-by">Counting groups of data without <code>GROUP BY</code></h3>
<p>Now, what I want is to find the group of data, where the last dated <code>field_a</code> is <code>null</code> but the other fields are not null. So that means, I want to return group <code>x</code>.</p>
<p>To do this, I start by knowing the total number of items within a group but <strong>without</strong> using <code>GROUP BY</code>.</p>
<pre><code class="lang-sql">count(external_id) over (
    partition by external_id
) as total_in_group,
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707171536889/a869be7a-febb-47fe-a8ec-bf4703a9953a.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-ordering-data-within-groups-without-group-by">Ordering data within groups without <code>GROUP BY</code></h3>
<p>Next, I want to order the items in the group by date using the <code>row_number()</code> function.</p>
<pre><code class="lang-sql">row_number() over(
    partition by external_id
    order by
        external_id,
        date
) as rownum
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707171575609/04c78a16-a9b0-4576-a390-317351024916.png" alt class="image--center mx-auto" /></p>
<p>And now, it's like everything has fallen into place. All I need to do to find ID 3, is find where <code>total_in_group</code> = <code>rownum</code> and <code>field_a IS NULL</code>. That's it! 🎉. Throw in a subquery. Life is good.</p>
<p>Well, life may not be performant. I did do this on just 6 rows of data. At work, we were dealing with millions of rows, and Postgres, where <code>isnull</code> does not exist 😒. Anywho. Can't complain.</p>
<p>Although, it's still not quite correct. It would still work if the row before it contained a <code>null</code> field. But I was satisfied I was on the right track to solve the problem with a little more digging through the manual.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707205643708/089e0fb9-8cc3-435d-9456-1636fa2ab4a9.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-knowing-where-to-find-solutions">Knowing where to find solutions</h2>
<p>I learn best by doing. I can't just read snippets and start writing code.</p>
<p>I need to understand what I'm reading in the context of building something tangible.</p>
<p>However, my faux pas is once I've grappled with the basics, I never look at the docs again. Where I've gone wrong, is I've read docs to solve a specific problem I had at the time.</p>
<p>Instead, Aaron's perspective is to read the docs to build a <strong>mental map</strong> of where to find solutions to problems we have in the future, not to understand everything in the docs today.</p>
<p>One example recently was at some point last year (2023) I came across an AWS article about <a target="_blank" href="https://aws.amazon.com/blogs/storage/point-in-time-recovery-and-continuous-backup-for-amazon-rds-with-aws-backup/">Point in Time Recovery</a>. I didn't even read it all.</p>
<p>While having a chat with our engineer, I brought up this article. As an alternative to another solution that worked in the past, it seemed both faster and simpler to implement! Now I couldn't tell him <em>how</em> it worked under the hood or <em>how</em> to implement it in Terraform or even via the GUI, purely knowing it was possible and where to find it was enough to make the suggestion!</p>
<p>Knowing where to find solutions, or that a solution exists, is a good reason to read the docs.</p>
<h2 id="heading-learning-to-read-the-docs-for-leisure">Learning to read the docs for leisure</h2>
<p>Now, I just can't see myself, doing some pre-bed reading of Nodejs, albeit, for this exercise I did.</p>
<p>The recommendation of printing documentation and reading it through? I'm not sure about that. I love the visceral nature of physical books and writing by pen, but I also hate having unbounded paper everywhere.</p>
<p>Yet, searching for a solution under time pressure limits our creativity (as deeply explained in <a target="_blank" href="https://chrisbailey.com/hyperfocus/">Hyperfocus</a> by Chris Bailey). With a deadline, we narrow our focus to get the task done in the quickest time possible. So with a deadline in place...</p>
<p>Yes. I get things done.</p>
<p>No. It didn't look pretty.</p>
<p>Yes. I've produced many-a-bad solution while under a deadline, even if the thing works on the surface.</p>
<p>In comparison, sitting, leisurely reading, I'm just learning for fun.</p>
<p>Reading the docs for leisure is a very different way of absorbing information. It makes me think, the reason why many, including myself, shy away from reading the docs is because <strong>we read it when we need it</strong>, and so we don't have time to wade our way through the superfluous abundance of information. We need a solution, now!</p>
<p>Putting that to the test, I went back to read the docs just a little more, and I discovered that my query could be corrected.</p>
<p>Remember, I want to select a group, where the last row in the group by date, has a nullable field, but the row before is not null.</p>
<p>Here's a more complete set, where we don't want group <code>x</code> or <code>y</code>, we want <code>z</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707206170558/593a4b4a-c0ea-4a93-bb1e-cbf1ed9dc1ff.png" alt class="image--center mx-auto" /></p>
<p>And voila 🥳.</p>
<pre><code class="lang-sql"><span class="hljs-keyword">select</span>
    *
<span class="hljs-keyword">from</span>
    (
        <span class="hljs-keyword">select</span>
            *,

            <span class="hljs-keyword">count</span>(external_id) <span class="hljs-keyword">over</span> (
                <span class="hljs-keyword">partition</span> <span class="hljs-keyword">by</span> external_id
            ) <span class="hljs-keyword">as</span> total_in_group,

            row_number() <span class="hljs-keyword">over</span>(
                <span class="hljs-keyword">partition</span> <span class="hljs-keyword">by</span> external_id
                <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span>
                    external_id,
                    <span class="hljs-built_in">date</span>
            ) <span class="hljs-keyword">as</span> <span class="hljs-keyword">rownum</span>,

            <span class="hljs-keyword">lead</span>(<span class="hljs-keyword">isnull</span>(field_a)) <span class="hljs-keyword">over</span>(
                <span class="hljs-keyword">partition</span> <span class="hljs-keyword">by</span> external_id
                <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span>
                    external_id,
                    <span class="hljs-built_in">date</span>
            ) <span class="hljs-keyword">as</span> nullable
        <span class="hljs-keyword">from</span>
            test.my_table
        <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span>
            external_id,
            <span class="hljs-keyword">rownum</span>
    ) t1
<span class="hljs-keyword">where</span>
    (total_in_group - <span class="hljs-number">1</span>) = <span class="hljs-keyword">rownum</span>
    <span class="hljs-keyword">and</span> nullable = <span class="hljs-number">1</span>
    <span class="hljs-keyword">and</span> field_a <span class="hljs-keyword">is</span> <span class="hljs-keyword">not</span> <span class="hljs-literal">null</span>;
</code></pre>
<p>The addition was to use the <code>LEAD</code> function which gets the next record along. So I could then check if the next record after is nullable, and thus select ID 2. I could also find the second to last field using <code>(total_in_group - 1) = rownum</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1707173550349/a531a972-764a-4ab7-a116-024d3e30ffc2.png" alt class="image--center mx-auto" /></p>
<p>Yes, it's a little more complex, but it's great there are a few ways to solve this problem. And I've banked yet another MySQL function.</p>
<h2 id="heading-when-reading-the-docs-is-bad">When reading the docs is bad!</h2>
<p>So, reading the docs is great! Or is it?</p>
<p>Time is precious. And so we thank the developer who took the time to distil information in a format that could be read, over and over again to make his or her time scale rather than having to explain things constantly.</p>
<p>However, too much documentation might be a problem. In fact, in <a target="_blank" href="https://twitter.com/nandoyum">Fernando Villalba</a>'s article, I still can't believe the video he shared is genuine, but here's an example of verbose documentation gone wrong.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.youtube.com/watch?v=1BB6wj6RyKo">https://www.youtube.com/watch?v=1BB6wj6RyKo</a></div>
<p> </p>
<p>Fernando pointed out:</p>
<blockquote>
<p>"<em>If reading the manual is the only way for you to understand every functionality of your application, you have failed at design.</em>"</p>
</blockquote>
<p>Too much documentation may be hiding bad design. Of course, with something like learning a programming language to solve a specific problem, it's rare for the language to be intuitive to just write it.</p>
<p>Intellisense helps.</p>
<p>Perhaps <a target="_blank" href="https://github.com/features/copilot">Copilot</a> does too.</p>
<p>But outside of this, you have to read the manual. However, the tools we use, <em>should</em> be as intuitive as possible, so their function doesn't require having to read the manual to get started.</p>
<p>If they're not, we have an opportunity to help the maintainers:</p>
<ol>
<li><p>improve their tools so they are intuitive to use. Too much documentation might be hiding bad design.</p>
</li>
<li><p>improve their docs by documenting <a target="_blank" href="https://nandovillalba.medium.com/the-art-of-the-handover-66256983960f">contextually</a> vs exhaustively, where we cannot make the tool more intuitive.</p>
</li>
<li><p>write external tutorials where we cannot improve the docs for whatever reason.</p>
</li>
</ol>
<p>And what do we get out of it?</p>
<ul>
<li><p>More skill[man/woman]ship.</p>
</li>
<li><p>More proficiency.</p>
</li>
<li><p>Known for mastery of a tool or technology which is great for our careers.</p>
</li>
<li><p>Better productivity.</p>
</li>
<li><p>Better design.</p>
</li>
<li><p>Better use of creativity.</p>
</li>
</ul>
<h2 id="heading-and-we-cant-read-everything">And we can't read everything</h2>
<p>Or can we?</p>
<p>Maybe if we just use a few tools instead of many, we can read all the docs, no problem.</p>
<p>But where I work, with so many tools and features being developed, I could spend all my time <a target="_blank" href="https://www.linkedin.com/pulse/how-build-team-commitment-reading-documentation-tammy-bjelland-cptd/?trk=articles_directory">reading the docs</a>, PRs, release notes and RFCs. I'd worry I'd get little to no work done.</p>
<p>So I need to be selective about what docs I read through.</p>
<h2 id="heading-what-docs-am-i-going-to-start-reading">What docs am I going to start reading?</h2>
<p>I didn't want to do a MySQL tutorial, but quite the opposite. I was inspired by <a target="_blank" href="https://aaronfrancis.com/2023/read-the-docs-like-a-book">"Read the Docs Like a Book"</a> article (another Aaron Francis special) which recommends becoming well acquainted with a set of documentation. The idea is that as you become more acquainted with your tools through the docs, you become a more skilled developer.</p>
<p>So, I decided I would "Read the Docs", and share notes on anything interesting. By interesting, it's mostly going to be things that are "new to me" or quirky. My current list to read contains:</p>
<ul>
<li><p><a target="_blank" href="https://nodejs.org/en"><strong>Nodejs</strong></a> because I still don't understand <a target="_blank" href="https://nodejs.org/api/buffer.html">Buffers</a> and I'm surprised there's a native way to run <a target="_blank" href="https://nodejs.org/api/test.html">tests</a>. I also think I should try and master it in at least one language, seeing as I love it so much.</p>
</li>
<li><p><a target="_blank" href="https://nuxt.com/"><strong>Nuxt</strong></a> because I love how it makes working with <a target="_blank" href="https://vuejs.org/">Vue</a> easy but I couldn't tell you what a <a target="_blank" href="https://nuxt.com/docs/api/composables/use-app-config">Composable</a> is.</p>
</li>
<li><p><a target="_blank" href="https://json-schema.org/"><strong>JSON Schema</strong></a> because I'm building <a target="_blank" href="https://github.com/gemmadlou/tin">Tin</a> and it'll help with standardising data formats.</p>
</li>
<li><p><a target="_blank" href="https://www.rust-lang.org/"><strong>Rust</strong></a> because I have a goal to learn it this year.</p>
</li>
<li><p><a target="_blank" href="https://cpp-lang.net/"><strong>C++</strong></a> because I've been a developer long enough, I should eventually learn it, and it's a stretch goal.</p>
</li>
<li><p><a target="_blank" href="https://kubernetes.io/"><strong>K8s</strong></a> because I use it almost daily and still don't know how to use <a target="_blank" href="https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/">labels</a> and metadata properly.</p>
</li>
<li><p><a target="_blank" href="https://jqlang.github.io/jq/"><strong>JQ</strong></a> because it's powerful and I love working on the CLI when testing APIs with <a target="_blank" href="https://httpie.io/">HTTPie</a>. HTTPie all day over Postman!</p>
</li>
<li><p><a target="_blank" href="https://aws.amazon.com/"><strong>AWS</strong></a> because it is the Cloud!</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to set up Pirsch Analytics in Nuxt 2]]></title><description><![CDATA[You may have your reasons for searching out an alternative to Google Analytics. If you want to get set up with Pirsch Analytics and Nuxt 2, you'll find the process very quick.
Pre-requisites

You will need a Nuxt application hosted on a live domain n...]]></description><link>https://gemmablack.dev/how-to-set-up-pirsch-analytics-in-nuxt-2</link><guid isPermaLink="true">https://gemmablack.dev/how-to-set-up-pirsch-analytics-in-nuxt-2</guid><category><![CDATA[Nuxt]]></category><category><![CDATA[analytics]]></category><category><![CDATA[pirsch]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Fri, 14 Apr 2023 13:31:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1681479224152/28be491b-effa-4440-bfb5-1b62907adfa9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>You may have your reasons for searching out an alternative to Google Analytics. If you want to get set up with <a target="_blank" href="https://pirsch.io/">Pirsch Analytics</a> and <a target="_blank" href="https://nuxtjs.org/">Nuxt 2</a>, you'll find the process very quick.</p>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<ul>
<li>You will need a Nuxt application hosted on a live domain name. It won't work locally.</li>
</ul>
<h2 id="heading-step-1-getting-set-up-with-pirsch">Step 1. Getting set up with Pirsch</h2>
<p>Sign up with Pirsch through <a target="_blank" href="https://pirsch.io/">https://pirsch.io/</a> and verify your email.</p>
<p>They'll take you through an onboarding process where you can add your domain name. Make sure to include <code>www</code> if that's where your naked domain redirects to.</p>
<p>They'll show you a Script tag that you can copy that looks like this:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">defer</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://api.pirsch.io/pirsch.js"</span>
    <span class="hljs-attr">id</span>=<span class="hljs-string">"pirschjs"</span>
    <span class="hljs-attr">data-code</span>=<span class="hljs-string">"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<h2 id="heading-step-2-loading-their-script-in-nuxt">Step 2. Loading their script in Nuxt</h2>
<p>With Nuxt, they use <a target="_blank" href="https://vue-meta.nuxtjs.org/">Vue Meta</a>, the HTML Metadata manager for Vue.js. This allows you to inject scripts or stylesheets amongst other things into the header of your SPA, universal or static app.</p>
<p>Add the following script block through the <code>nuxt.config.js</code> .</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  ...
  head: {
    <span class="hljs-attr">script</span>: [{
      <span class="hljs-attr">src</span>: <span class="hljs-string">'https://api.pirsch.io/pirsch.js'</span>,
      <span class="hljs-string">'data-code'</span>: <span class="hljs-string">'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'</span>,
      <span class="hljs-attr">type</span>: <span class="hljs-string">'text/javascript'</span>,
      <span class="hljs-attr">id</span>: <span class="hljs-string">'pirschjs'</span>,
      <span class="hljs-attr">defer</span>: <span class="hljs-literal">true</span>
    }],
   ...
}
</code></pre>
<p>Change the <code>data-code</code> to match the code from Pirsch.</p>
<h2 id="heading-step-3-deploy-and-wait">Step 3. Deploy and wait</h2>
<p>Within minutes of deploying, we had cookieless, privacy-first analytics. It took longer to write this article than it took to make the change and get set up with Pirsch 😂.</p>
<blockquote>
<h2 id="heading-disclaimer-why-not-google-analytics">Disclaimer: Why not Google Analytics?</h2>
<p>Firstly, I just have to make sure I'm not mistaken; I have no hatred for Google or for Google Analytics. Yes, the mass data collection is a little creepy, but more importantly for me, I can't stomach the cookie (🍪) bombardment (💣) after GDPR came in. I don't want that experience for users at <a target="_blank" href="https://embodimentshop.com">embodimentshop.com</a> either.</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[Managing multiple htpasswd users with the Ingress-Nginx's auth-map annotation]]></title><description><![CDATA[I was asked to add multiple htpasswd users to a temporary demo service in Kubernetes. It took a little time to find the relevant documentation, so I decided to write this article (at least for my future self to remember).
The Kubernetes Nginx Ingress...]]></description><link>https://gemmablack.dev/managing-multiple-htpasswd-users-with-the-ingress-nginxs-auth-map-annotation</link><guid isPermaLink="true">https://gemmablack.dev/managing-multiple-htpasswd-users-with-the-ingress-nginxs-auth-map-annotation</guid><category><![CDATA[Kubernetes]]></category><category><![CDATA[nginx ingress]]></category><category><![CDATA[Bash]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Fri, 17 Mar 2023 18:57:52 GMT</pubDate><content:encoded><![CDATA[<p>I was asked to add multiple <a target="_blank" href="https://httpd.apache.org/docs/2.4/programs/htpasswd.html">htpasswd</a> users to a temporary demo service in Kubernetes. It took a little time to find the relevant documentation, so I decided to write this article (at least for my future self to remember).</p>
<p>The <a target="_blank" href="https://github.com/kubernetes/ingress-nginx">Kubernetes Nginx Ingress</a> has an <strong>auth-file</strong> annotation which is excellent for single-user credentials, but the <strong>auth-map</strong> annotation was designed for easily adding multiple users.</p>
<blockquote>
<p>Disclaimer: I'm on MacOS. So the commands may not work the same on a different operating system. Hopefully, the process can be replicated.</p>
</blockquote>
<p>In this article, I'll share:</p>
<ul>
<li><p>A way to generate the htpasswd usernames and hashed passwords</p>
</li>
<li><p>How to define a multi-user htpasswd manifest yaml for the ingress nginx</p>
</li>
<li><p>How to configure the Ingress Nginx auth-map annotation.</p>
</li>
</ul>
<h2 id="heading-pre-requisites">Pre-requisites</h2>
<ul>
<li><p>You need a K8s cluster setup with an Ingress Nginx</p>
</li>
<li><p>You need a deployment and service to hide behind an htpasswd.</p>
</li>
</ul>
<h2 id="heading-step-1-creating-a-list-of-usernames-and-passwords">Step 1. Creating a list of usernames and passwords</h2>
<p>You could autogenerate user names in any way you like. I used a website to generate passwords and a spreadsheet to collate them.</p>
<p>Nonetheless, we're good to go as long as you have a simple CSV file with the following columns.</p>
<pre><code class="lang-yaml"><span class="hljs-string">num,username,password</span>
<span class="hljs-number">1</span><span class="hljs-string">,john,random01</span>
<span class="hljs-number">2</span><span class="hljs-string">,smith,random02</span>
</code></pre>
<p><strong>Note, in our version, the password should be 8 characters or less due to using the md5 hash.</strong></p>
<h2 id="heading-step-2-generating-the-hashed-passwords">Step 2. Generating the hashed passwords</h2>
<p>Create a bash file.</p>
<pre><code class="lang-bash">touch hash.sh;
chmod +x ./hash.sh
</code></pre>
<p>Add the following code:</p>
<pre><code class="lang-bash"><span class="hljs-meta">#!/bin/bash</span>
ed -s <span class="hljs-variable">$1</span> &lt;&lt;&lt; w

<span class="hljs-built_in">exec</span> &lt; <span class="hljs-variable">$1</span>
<span class="hljs-built_in">read</span> header
<span class="hljs-keyword">while</span> IFS=<span class="hljs-string">","</span> <span class="hljs-built_in">read</span> -r num username password
<span class="hljs-keyword">do</span>
    hashed=`openssl passwd -quiet <span class="hljs-variable">$password</span>`
    <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$username</span>: <span class="hljs-variable">$hashed</span>"</span>
<span class="hljs-keyword">done</span>
</code></pre>
<p>To explain how it works:</p>
<ul>
<li><p><code>exec &lt; $1</code> allows us to pass in a file path as an argument.</p>
</li>
<li><p><code>ed -s $1 &lt;&lt;&lt; w</code> adds an extra line to the end of the CSV if one doesn't exist. This is to ensure when we loop over the CSV rows, we loop over every row. Otherwise, it will miss the final row in the spreadsheet.</p>
</li>
<li><p><code>openssl passwd -quiet $password</code> creates a simple md5 hashed password. The <code>-quiet</code> is because of a truncation error warning that's irrelevant if the password is 8 characters or less.</p>
</li>
<li><p><code>$username: $hashed</code> is just so we have something we can copy-paste into our Kubernetes secret file</p>
</li>
</ul>
<p>Now, we can run the <code>hash.sh</code> script to generate the passwords.</p>
<pre><code class="lang-bash">./hash.sh ./passwords.csv
</code></pre>
<p>The output will look something like this.</p>
<pre><code class="lang-bash">john: XQIeryMOkCNpk
smith: gB1FNtHU3drCA
</code></pre>
<h2 id="heading-step-3-creating-the-kubernetes-secret-file">Step 3. Creating the Kubernetes secret file</h2>
<p>We can take the output of the <code>hash.sh</code> command and add the key-value pairs under the <code>stringData</code> property.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">apiVersion:</span> <span class="hljs-string">v1</span>
<span class="hljs-attr">kind:</span> <span class="hljs-string">Secret</span>
<span class="hljs-attr">type:</span> <span class="hljs-string">Opaque</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">secret-file</span>
<span class="hljs-attr">stringData:</span>
  <span class="hljs-attr">john:</span> <span class="hljs-string">XQIeryMOkCNpk</span>
  <span class="hljs-attr">smith:</span> <span class="hljs-string">gB1FNtHU3drCA</span>
</code></pre>
<p>You want to apply the secret file to the cluster.</p>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">&lt;secret-file-path&gt;</span>
</code></pre>
<h2 id="heading-step-4-configuring-the-ingress">Step 4. Configuring the ingress</h2>
<p>Now we have our usernames and passwords configured in a Kubernetes secret file, we need to attach them to the ingress. Add the following annotations to the ingress.</p>
<pre><code class="lang-yaml"><span class="hljs-attr">kind:</span> <span class="hljs-string">Ingress</span>
<span class="hljs-attr">metadata:</span>
  <span class="hljs-attr">name:</span> <span class="hljs-string">project-ingress</span>
  <span class="hljs-attr">annotations:</span>    
    <span class="hljs-attr">nginx.ingress.kubernetes.io/auth-type:</span> <span class="hljs-string">basic</span>
    <span class="hljs-attr">nginx.ingress.kubernetes.io/auth-secret:</span> <span class="hljs-string">secret-file</span>
    <span class="hljs-attr">nginx.ingress.kubernetes.io/auth-secret-type:</span> <span class="hljs-string">auth-map</span>
    <span class="hljs-attr">nginx.ingress.kubernetes.io/auth-realm:</span> <span class="hljs-string">"Progress Authentication"</span>
    <span class="hljs-comment">#...etc</span>
</code></pre>
<ul>
<li><p><code>nginx.ingress.kubernetes.io/auth-type: basic</code> tells the ingress to use Basic authentication versus Digest auth.</p>
</li>
<li><p><code>nginx.ingress.kubernetes.io/auth-secret: secret-file</code> is the name of our secrets file which we defined earlier.</p>
</li>
<li><p><code>nginx.ingress.kubernetes.io/auth-secret-type: auth-map</code> tells the ingress to interpret the secrets a list of usernames and hashed passwords versus a single htpasswd file.</p>
</li>
<li><p><code>nginx.ingress.kubernetes.io/auth-realm: "Progress Authentication"</code> returns a <code>WWW-Authenticate"</code> header with whatever value you define.</p>
</li>
</ul>
<p>Finally, you want to apply your ingress.</p>
<pre><code class="lang-yaml"><span class="hljs-string">kubectl</span> <span class="hljs-string">apply</span> <span class="hljs-string">-f</span> <span class="hljs-string">&lt;ingress-file-path&gt;</span>
</code></pre>
<h2 id="heading-step-5-testing-the-login">Step 5. Testing the login</h2>
<p>Now, when you visit the page, you'll be presented with a sign-in form, of which, you can test the credentials.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1679079003107/0cc99cb0-bbb7-4168-8893-33bf68a9326f.png" alt class="image--center mx-auto" /></p>
<p>If you're testing via the GUI, you may want to use Roland Toth's advice to <a target="_blank" href="https://blog.rolandtoth.hu/logout-from-htpasswd-protected-site/">log out:</a></p>
<pre><code class="lang-bash">http://<span class="hljs-built_in">logout</span>@example.com/
</code></pre>
<p>Alternatively, you can test authentication through curl or <a target="_blank" href="https://httpie.io/">httpie</a>:</p>
<pre><code class="lang-bash">http https://example.com

http https://example.com --auth username:password
</code></pre>
<h2 id="heading-signing-out">Signing out</h2>
<p>If you're looking for a robust sign-in solution, perhaps <code>htpasswd</code> isn't as ideal as implementing or using an existing email-based username and password system. But for hiding functionality in testing environments that only a few people need access to, it's a handy and quick way to add protection to your website.</p>
<h2 id="heading-additional-resources">Additional resources</h2>
<p><a target="_blank" href="https://github.com/kubernetes/ingress-nginx/issues/5858">https://github.com/kubernetes/ingress-nginx/issues/5858</a></p>
<p><a target="_blank" href="https://github.com/kubernetes/ingress-nginx/pull/4560">https://github.com/kubernetes/ingress-nginx/pull/4560</a></p>
]]></content:encoded></item><item><title><![CDATA[How a Multi-AWS account helps to prevent accidental damage when using Terragrunt]]></title><description><![CDATA[https://media.giphy.com/media/lJDLy8JXZwkBa/giphy.gif
 
Confession time. I've been ignoring the suggestion of having multiple AWS accounts for years through AWS Organisations.
It seems I'm not the only one avoiding the leap:

"In the last few years.....]]></description><link>https://gemmablack.dev/how-multi-aws-account-helps-prevent-accidental-damage-when-using-terragrunt</link><guid isPermaLink="true">https://gemmablack.dev/how-multi-aws-account-helps-prevent-accidental-damage-when-using-terragrunt</guid><category><![CDATA[AWS]]></category><category><![CDATA[terragrunt]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sat, 11 Mar 2023 08:27:36 GMT</pubDate><content:encoded><![CDATA[<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://media.giphy.com/media/lJDLy8JXZwkBa/giphy.gif">https://media.giphy.com/media/lJDLy8JXZwkBa/giphy.gif</a></div>
<p> </p>
<p>Confession time. I've been ignoring the suggestion of having multiple AWS accounts for years through <a target="_blank" href="https://aws.amazon.com/organizations/">AWS Organisations</a>.</p>
<p>It seems I'm not the only one avoiding the leap:</p>
<blockquote>
<p>"In the last few years...one thing we still find from time to time is customers running a single AWS account containing all their development and staging environments alongside their production workload." - <a target="_blank" href="https://www.scalefactory.com/about/meet-team/jon-topper/">John Topper</a> of <a target="_blank" href="https://www.scalefactory.com/blog/2021/11/29/should-you-be-using-more-than-one-aws-account/">Scale Factory</a></p>
</blockquote>
<p>But is having multiple environments in one account all that bad:</p>
<blockquote>
<p>"It may surprise you to hear that it’s entirely possible to run a perfectly secure, mature, and complex set of AWS resources out of a single AWS account. Yes, it’s true! IAM policies are infinitely customizable, and access can be controlled at an insanely granular level. Tags can be enforced, and single-account service limits can (usually) be increased." - David Blocher of <a target="_blank" href="https://acloudguru.com/blog/engineering/5-tips-for-starting-your-organization-with-multiple-aws-accounts">A Cloud Guru</a></p>
</blockquote>
<p>So why I have made a U-turn on developing multiple AWS accounts?</p>
<p>David Blocher's article explains the inherent risks to security and the complexity of managing IAM credentials within a single account. This is very true as the development team grow bigger.</p>
<p>For me, however, his point about limiting the blast radius is what I'll briefly touch on today; not from a security perspective per se, but from the perspective of preventing accidental damage when managing infrastructure internally.</p>
<h2 id="heading-why-is-accidental-damage-an-issue-with-a-single-aws-account">Why is accidental damage an issue with a single AWS account?</h2>
<p>With a growing tech team and more people needing to test infrastructure, it's not very safe to allow automated tools like Terragrunt to run riot in an environment where production also exists. Having somewhere completely isolated to test first is beneficial to prevent accidents from occurring.</p>
<blockquote>
<p>"As your developers are building things, it’s pretty likely they’ll be building and destroying resources multiple times a day as they figure out what the solution will ultimately look like." - <a target="_blank" href="https://www.scalefactory.com/about/meet-team/jon-topper/">John Topper</a> of <a target="_blank" href="https://www.scalefactory.com/blog/2021/11/29/should-you-be-using-more-than-one-aws-account/">Scale Factory</a></p>
</blockquote>
<h3 id="heading-the-clash-of-the-regions">The clash of the regions</h3>
<p>One way to test infrastructure in a single account is by using an unused region.</p>
<p><em>It works well until it doesn't.</em></p>
<p>I recently was testing a <a target="_blank" href="https://registry.terraform.io/modules/terraform-aws-modules/vpc/aws/latest">Terraform VPC module</a> with <a target="_blank" href="https://terragrunt.gruntwork.io/">Terragrunt</a>. I didn't appreciate that it would use my default region to apply the root VPC without declaring the provider resource. Because of this, I launched my new VPC wrongly within the <code>eu-west-1</code> region, yet, the subnets were launched in the intended <code>us-east-1</code> region through a Terraform variable.</p>
<p>Thankfully, this was done in a separate account. But it goes to show a simple infrastructure test can have consequences on key environments despite best intentions.</p>
<h3 id="heading-the-clash-of-the-vpcs">The clash of the VPCs</h3>
<p>Another way to manage environments within a single account is to use separate VPCs. By default, VPCs cannot communicate with each other. The networks are isolated, and they have their own sets of security groups for access.</p>
<p>However, you have to be aware of overlapping CIDRs again. While you can create multiple VPCs with the same CIDRs and AWS allows it, you can never peer these networks together. Chances are, if you're using it for separate environments anyway, you may never want to peer them, but you never know when a requirement comes up, even temporarily to connect VPCs.</p>
<h3 id="heading-managing-least-privileges">Managing least-privileges</h3>
<p>There are a few recommended ways of managing privileges in AWS. One is to start writing a policy with least privilege manually, and grant permissions when and as you need them during development. However, manually doing this can be time-costly. You can spend ages working out what custom policies to create.</p>
<p>Alternatively, you can create a fully privileged IAM user, do all the work you need and then run the <a target="_blank" href="https://aws.amazon.com/iam/features/analyze-access">IAM Access Analyzer</a> to determine the permissions for you. Of course, doing this in a production account is risky at best! But with a sandboxed developer account, you can test this all day long and then narrow down the secrets to what you need without manually configuring policies from scratch.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<iframe src="https://giphy.com/embed/S6TEoUBJuGfQCoGl8l" width="480" height="270" class="giphy-embed"></iframe>

<p><a target="_blank" href="https://giphy.com/gifs/razer-relax-peaceful-S6TEoUBJuGfQCoGl8l">via GIPHY</a></p>
<p>With a small team, or a single Ops engineer managing things, multiple accounts seem like an unnecessary headache. However, if your team is growing and you're wondering how to test automation tasks safely, I highly recommend at least one other sandbox or test environment account for that before moving anything to production for a peaceful life.</p>
]]></content:encoded></item><item><title><![CDATA[Terragrunt secrets management with Doppler]]></title><description><![CDATA[Why Terragrunt and Doppler
With Infrastructure as Code, you may find yourself facing a circular dependency loop when it comes to secrets. Some services you self-host to help manage business applications won't exist before you create the infrastructur...]]></description><link>https://gemmablack.dev/terragrunt-secrets-management-with-doppler</link><guid isPermaLink="true">https://gemmablack.dev/terragrunt-secrets-management-with-doppler</guid><category><![CDATA[terragrunt]]></category><category><![CDATA[Terraform]]></category><category><![CDATA[#IaC]]></category><category><![CDATA[doppler]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Tue, 07 Mar 2023 09:22:34 GMT</pubDate><content:encoded><![CDATA[<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1680873092952/2cb9d0c2-44b2-4ea7-996d-9ddf38036279.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-why-terragrunt-and-doppler">Why Terragrunt and Doppler</h3>
<p>With Infrastructure as Code, you may find yourself facing a circular dependency loop when it comes to secrets. Some services you self-host to help manage business applications won't exist before you create the infrastructure. So you need a place for infrastructure secrets.</p>
<p>Thankfully there are lots of options. We can use encrypted files, either within versional control or saved somewhere externally. There are managed solutions such as <a target="_blank" href="https://cloud.hashicorp.com/products/vault">HCP Vault</a> or AWS Secrets Manager.</p>
<p>It was clear to see that Doppler is a great utility for developers to share secrets. But it's not a stretch to realise that Doppler can work for infrastructure secrets too.</p>
<p>In this article, I share the few steps required to get Doppler working with Terragrunt.</p>
<h2 id="heading-managing-secrets-with-terragrunt">Managing secrets with Terragrunt</h2>
<p>Terragrunt proposes <a target="_blank" href="https://blog.gruntwork.io/a-comprehensive-guide-to-managing-secrets-in-your-terraform-code-1d586955ace1">3 ways of managing secrets</a>.</p>
<ol>
<li><p>Environment variables</p>
</li>
<li><p>Encrypted files</p>
</li>
<li><p>Secret stores</p>
</li>
</ol>
<p>With Doppler, we can combine the simple use of <em>Environment variables</em> with a <em>secret store</em>.</p>
<h2 id="heading-using-environment-variables-with-doppler">Using environment variables with Doppler</h2>
<h3 id="heading-step-1-defining-the-environment-variable-within-our-terragrunt-file">Step 1: Defining the environment variable within our Terragrunt file</h3>
<p>In the example Terragrunt file, we've decided to use an environment variable to prefix our bucket name. In a real-world example, you may have secrets or local variables that you don't want inside version control.</p>
<p>Reference your variable within a <code>terragrunt.hcl</code> by using the <code>get_env</code> function:</p>
<pre><code class="lang-bash">remote_state {
  <span class="hljs-comment"># ...</span>
  config = {
    bucket = <span class="hljs-string">"<span class="hljs-variable">${get_env("TG_BUCKET_PREFIX", "")}</span>-infrastructure"</span>

    key = <span class="hljs-string">"terraform/<span class="hljs-variable">${path_relative_to_include()}</span>/terraform.tfstate"</span>
    region = <span class="hljs-string">"<span class="hljs-variable">${local.global.bucket_region}</span>"</span>
    encrypt = <span class="hljs-literal">true</span>

  }
}
</code></pre>
<h3 id="heading-step-2-adding-the-environment-variable-to-doppler">Step 2. Adding the environment variable to Doppler</h3>
<p>Once logged into Doppler, you can select a project, then the project environment (eg. production) and add your environment variable there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1678216281223/052ab322-ccb3-4cc3-a306-bab34e379b1e.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-step-3-configuring-doppler-on-your-terragrunt-project">Step 3. Configuring Doppler on your Terragrunt project</h3>
<p>To be able to use Doppler within your project, you must first set up Doppler to connect it to the right project and environment.</p>
<p>Run the setup command and select a project:</p>
<pre><code class="lang-bash">doppler setup

<span class="hljs-comment"># &gt; Select a project:  [Use arrows to move, type to filter]</span>
</code></pre>
<p>Select an environment (eg. production).</p>
<pre><code class="lang-bash"><span class="hljs-comment"># &gt; Select a config:  [Use arrows to move, type to filter]</span>
</code></pre>
<p>Once done, you can view the secrets from Doppler by running:</p>
<pre><code class="lang-bash">doppler secrets
</code></pre>
<h3 id="heading-step-4-injecting-doppler-environment-variables-into-terragrunt-commands">Step 4. Injecting Doppler environment variables into Terragrunt commands</h3>
<p>Finally, to run Terragrunt and inject secrets, we need to prefix the Terragrunt command with the Doppler <code>run</code> command.</p>
<pre><code class="lang-bash">doppler run -- terragrunt run-all plan
</code></pre>
<h2 id="heading-finished">Finished</h2>
<p>In conclusion, using Doppler for Terragrunt secrets management is a powerful and effective way to ensure that your infrastructure as code is secure, well-protected and straightforward to use.</p>
<h3 id="heading-next-steps">Next steps</h3>
<ul>
<li>How to get started with <a target="_blank" href="https://docs.doppler.com/docs/demo-videos">Doppler</a>.</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[How to create a Webpack 5 Loader with Preons]]></title><description><![CDATA[Why a Webpack Loader
Webpack helps make frontend development more streamlined and recently, I wanted to transform YAML into CSS as part of the build process using the Preons library.
By the end of this article:

you will create a Webpack loader that ...]]></description><link>https://gemmablack.dev/css-yaml-loader-preons-webpack-5</link><guid isPermaLink="true">https://gemmablack.dev/css-yaml-loader-preons-webpack-5</guid><category><![CDATA[webpack]]></category><category><![CDATA[CSS]]></category><category><![CDATA[Yarn]]></category><dc:creator><![CDATA[Gemma Black]]></dc:creator><pubDate>Sat, 02 Jan 2021 19:49:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1609616911597/5USLexYF4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="why-a-webpack-loader">Why a Webpack Loader</h3>
<p><a target="_blank" href="https://webpack.js.org/">Webpack</a> helps make frontend development more streamlined and recently, I wanted to transform YAML into CSS as part of the build process using the <a target="_blank" href="https://github.com/preons/preons">Preons</a> library.</p>
<p>By the end of this article:</p>
<ul>
<li>you will create a Webpack loader that converts a YAML configuration file into CSS</li>
<li>you will manually test the loader against a basic HTML page</li>
<li>you will know how to create your own Webpack loader in future</li>
</ul>
<h3 id="background">Background</h3>
<p>Preons (and disclaimer, a project of mine) is a functional CSS utility library. You configure the utility classes in YAML.</p>
<p>Webpack is a popular module bundler, and one helpful thing it does is transform <a target="_blank" href="https://sass-lang.com/">Sass</a> into CSS with a few useful community loaders.</p>
<h2 id="pre-requisites">Pre-requisites</h2>
<p>To follow this tutorial, you need:</p>
<ul>
<li>Node 12+. <a target="_blank" href="https://nodejs.org/en/">Install Node's</a> command-line tool.</li>
<li>Yarn. <a target="_blank" href="https://yarnpkg.com/getting-started/install">Install Yarn</a>'s a command-line tool.</li>
<li>Have a basic knowledge of javascript</li>
</ul>
<h2 id="step-1-creating-a-new-project">Step 1: Creating a new project</h2>
<p>You will create a basic empty project and initialise npm to begin installing local node packages.</p>
<p>Execute the following command to create a new project folder:</p>
<pre><code>mkdir custom-webpack-loader &amp;&amp; <span class="hljs-built_in">cd</span> custom-webpack-loader
</code></pre><p>Initialise Node's package manager:</p>
<pre><code>yarn <span class="hljs-keyword">init</span>
</code></pre><p>You will be asked a series of questions:</p>
<pre><code class="lang-bash">yarn init v1.22.4
question name (custom-webpack-loader): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author: 
question license (MIT): 
question private: 
success Saved package.json
</code></pre>
<p>By the end, you will have a basic <code>package.json</code> without any dependencies. But you'll add them in a few step.</p>
<h2 id="step-2-configuring-webpack">Step 2 - Configuring Webpack</h2>
<p>In this step, you will create a webpack configuration file and tweak it to load a YAML file. It will allow you to test the loader as if you were using it in a project.</p>
<p>Install Webpack and Webpack's CLI to give you access to the Webpack builder:</p>
<pre><code class="lang-bash">yarn add webpack webpack-cli
</code></pre>
<p>Next, you'll create the <code>webpack.config.js</code>:</p>
<pre><code class="lang-bash">touch webpack.config.js
</code></pre>
<p>In <code>webpack.config.js</code>, add:</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);

<span class="hljs-built_in">module</span>.exports = {
  <span class="hljs-comment">//...</span>
  <span class="hljs-attr">mode</span>: <span class="hljs-string">"development"</span>,
  <span class="hljs-attr">entry</span>: path.resolve(__dirname, <span class="hljs-string">"fixtures"</span>) + <span class="hljs-string">"/preons.js"</span>,
  <span class="hljs-attr">module</span>: {
    <span class="hljs-attr">rules</span>: [
      {
        <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.ya?ml$/</span>,
        use: [
          <span class="hljs-string">"style-loader"</span>,
          <span class="hljs-string">"css-loader"</span>,
          {
            <span class="hljs-attr">loader</span>: path.resolve(__dirname, <span class="hljs-string">"lib"</span>) + <span class="hljs-string">"/loader.js"</span>
          },
          <span class="hljs-string">"yaml-loader"</span>,
        ],
      },
    ],
  },
};
</code></pre>
<ul>
<li>The <code>mode</code> property tells Webpack to make optimisations based on environments. In production, however, you would change the <code>mode</code> to <code>production</code>. However, for this use case, the default will be <code>development</code>.</li>
<li>Notice how the <code>.module.rules[0].test</code> catches <code>.yaml</code> and <code>.yml</code> files based on regex rules.</li>
<li>The <code>entry</code> file points to a  fixtures directory as that directory has files for you to test.</li>
<li>There are four loaders, 3 of which are existing npm packages. Webpack will apply rules from bottom to top, so first, the <code>yaml-loader</code> will take YAML and convert it into JSON. Your loader will transform Javascript into CSS; afterwards, the <code>css-loader</code> will apply its transformation and finally, the <code>style-loader</code> takes css and puts it into style blocks that are inserted into your page through Webpack's script.</li>
</ul>
<p>Install other dependency loaders using yarn:</p>
<pre><code class="lang-bash">yarn add style-loader css-loader yaml-loader
</code></pre>
<p>Now you have a basic Webpack config for testing the loader you're going to build.</p>
<h2 id="step-3-importing-yaml-from-our-input-js-file">Step 3 - Importing YAML from our input js file</h2>
<p>When initially learning about Webpack, one of the strange conventions is not to deal with non-javascript files through the configuration. Instead, you import them as if they are javascript modules themselves. You'll do the same here.</p>
<p>First. Create the <code>fixtures</code> directory:</p>
<pre><code><span class="hljs-keyword">mkdir</span> fixtures
</code></pre><p>Next, you need a Preons config file. To save you from learning the syntax and configuring one yourself, install the Preons utility globally.</p>
<pre><code class="lang-bash">npm install preons -g
</code></pre>
<p>Generate the Preons YAML config:</p>
<pre><code class="lang-bash">preons config &gt; fixtures/preons.yaml
</code></pre>
<p>You will see a YAML file containing CSS class rules such as <code>theme-colors</code>, scales and percentages. The file contents should start like this:</p>
<pre><code class="lang-yaml"><span class="hljs-attr">preons:</span>
  <span class="hljs-attr">baseline:</span> <span class="hljs-number">1.</span><span class="hljs-string">6rem</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-attr">theme-colors:</span>
      <span class="hljs-attr">black:</span> <span class="hljs-string">"#000000"</span>
      <span class="hljs-attr">white:</span> <span class="hljs-string">"#ffffff"</span>
      <span class="hljs-attr">transparent:</span> <span class="hljs-string">transparent</span>
      <span class="hljs-attr">hotpinkxl:</span> <span class="hljs-string">"#f188bc"</span>
</code></pre>
<p>Create <code>fixtures/preons.js</code>. </p>
<pre><code class="lang-bash">touch fixtures/preons.js
</code></pre>
<p>Add a require expression to it, so you import the YAML through it.</p>
<pre><code class="lang-js"><span class="hljs-built_in">require</span>(<span class="hljs-string">"./preons.yaml"</span>);
</code></pre>
<p>Now you have configured the webpack loader to take the <code>fixtures/preons.js</code> file and run any YAML imports through the <code>lib/loader.js</code>, and it will spit that back out inside a <code>dist</code> folder. But before that, you'll need the loader.</p>
<h2 id="step-4-creating-the-loader">Step 4 - Creating the loader</h2>
<p>Now you can build the <code>loader.js</code>. The minimum is one exported default function that returns a <code>module.exports = &lt;&gt;</code> string.</p>
<p>First, create the <code>loader.js</code> file:</p>
<pre><code class="lang-bash">mkdir -p lib &amp;&amp; touch lib/loader.js
</code></pre>
<p>Add the basic method to test the loader is working:</p>
<pre><code class="lang-js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">source</span>) </span>{
  <span class="hljs-built_in">console</span>.log(source)
  <span class="hljs-keyword">return</span> <span class="hljs-string">`module.exports = {}`</span>;
};
</code></pre>
<p>At this point, you can run webpack, and you will see the YAML source load in:</p>
<pre><code class="lang-bash">npx webpack
</code></pre>
<p>The output will be a lot of JSON, because of the <code>console.log(source)</code>. You'll also find a <code>dist</code> folder in your project with a <code>main.js</code> file.</p>
<p>With the source containing JSON, transforming using the Preons library is only a few more steps. Install Preons locally into the project to access its lib functions to do the transformation of the <code>preons.yaml</code> into CSS:</p>
<pre><code class="lang-bash">yarn add preons
</code></pre>
<p>Change the <code>lib/loader.js</code> to parse the YAML JSON into CSS.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> preons = <span class="hljs-built_in">require</span>(<span class="hljs-string">"preons/src/lib/stylesheet"</span>);

<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">source</span>) </span>{
  <span class="hljs-keyword">let</span> set = <span class="hljs-built_in">JSON</span>.parse(source);
  <span class="hljs-keyword">let</span> css = preons({ set });
  <span class="hljs-keyword">return</span> <span class="hljs-string">`module.exports = <span class="hljs-subst">${css}</span>`</span>;
};
</code></pre>
<ul>
<li><code>let set = JSON.parse(source)</code> converts JSON into a javascript object required by the Preons <code>stylesheet</code> function.</li>
<li><code>let css = preons({ set });</code> is a Preons function that transforms the YAML into CSS.</li>
<li><code>return</code>module.exports = ${css}<code>;</code> returns the css as a module to be used by the rest of the Webpack pipeline ruleset.</li>
</ul>
<p>Now test your webpack pipeline by running:</p>
<pre><code class="lang-bash">npx webpack
</code></pre>
<p>If you look inside <code>dist/main.js</code>, you will find the entirety of Preons CSS generated. To check the generation works, do a grep for the css Property <code>bg-black</code>:</p>
<pre><code class="lang-bash">cat dist/main.js | grep <span class="hljs-string">"bg-black"</span>
</code></pre>
<h2 id="step-5-testing-the-webpack-loader-with-html">Step 5 - Testing the webpack loader with HTML</h2>
<p>Now you are generating CSS from the Preons YAML configuration file. You will want to test you can develop with it.</p>
<p>Create a raw HTML <code>index.html</code> file in the root of the project:</p>
<pre><code class="lang-bash">touch index.html
</code></pre>
<p>Then add a simple starter HTML5 file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-black"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://unpkg.com/browse/preons@0.4.5/dist/reset.css"</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"dist/main.js"</span> &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"bg-red"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Hi<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<ul>
<li>The <code>index.html</code> references Preons reset package as well as your <code>dist/main.js</code> file.</li>
<li>The markup has Preons utility classes such as <code>bg-black</code> and <code>bg-red</code>. Open the <code>index.html</code>, and it will look similar to this:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609059262499/POAzizHUP.png" alt="image.png" /></p>
<p>Finally, you want to test that we can make changes to our YAML and it produces CSS instantly.</p>
<p>Start the Webpack dev server to watch for changes using:</p>
<pre><code class="lang-bash">npx webpack --watch
</code></pre>
<p>Go into the <code>preons.yaml</code> and look for the <code>theme-color -&gt; black</code>. Change it from <code>black: "#000000"</code> to <code>black: "yellow"</code>. The file should have this:</p>
<pre><code class="lang-yaml"><span class="hljs-comment">#....</span>
  <span class="hljs-attr">rules:</span>
    <span class="hljs-attr">theme-colors:</span>
      <span class="hljs-attr">black:</span> <span class="hljs-string">"yellow"</span>
</code></pre>
<p>Refresh the page, and the <code>bg-black</code> rule is now <code>yellow</code>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1609059539994/DZXZWOfng.png" alt="image.png" /></p>
<p>Ok. You will want to keep <code>bg-black</code> as it was, but it's just to prove that the generation runs through Webpack. You can now add more colours, change colours or remove them. Of course, you can add any utilities you want using the configuration file. The documentation on how to use the configuration file is still under development, but I'll remember to add it here once it's done.</p>
<h2 id="conclusion">Conclusion</h2>
<p>You built a Webpack loader to ingest YAML and create CSS to be used as part of a development and production build. You used pre-existing modules (e.g. yaml-loader) to keep your module loader concise.</p>
<p>If you want to continue using Preons, look out for the <a target="_blank" href="https://www.npmjs.com/package/preons-webpack-loader">Preons webpack loader package</a>, so you won't have to maintain it yourself.</p>
<p>Do you have any ideas for your own for a webpack loader?</p>
<p>Also, I hope you enjoyed this article. I'd love feedback on how to improve as a writer and as a developer, so feel free to leave a comment or DM me on Twitter.</p>
]]></content:encoded></item></channel></rss>