<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Agustinus Nathaniel</title>
    <description>The latest articles on Forem by Agustinus Nathaniel (@agustinusnathaniel).</description>
    <link>https://forem.com/agustinusnathaniel</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F113961%2F41841a50-3d16-4cde-a39a-0b87b20c5156.jpg</url>
      <title>Forem: Agustinus Nathaniel</title>
      <link>https://forem.com/agustinusnathaniel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/agustinusnathaniel"/>
    <language>en</language>
    <item>
      <title>A Practical Guide to Data Standards for Seamless Collaboration and Data Integrity</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Wed, 24 Sep 2025 05:00:14 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/a-practical-guide-to-data-standards-for-seamless-collaboration-and-integrity-138k</link>
      <guid>https://forem.com/agustinusnathaniel/a-practical-guide-to-data-standards-for-seamless-collaboration-and-integrity-138k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;If you’ve ever worked on a product with multiple engineers across frontend and backend, you’ll know how quickly things can fall apart without shared rules for handling data. Small differences—like whether dates should be UTC or local, or if &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;[]&lt;/code&gt; mean the same thing—can create bugs that are painful to debug but trivial to avoid. I’ve been in that situation many times, and over the years I’ve settled on a set of lightweight standards that keep things predictable. This post is about those standards: pragmatic, easy-to-adopt rules that have saved me (and my teams) countless hours.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Data Standards Matter
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Everyone interprets data the same way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interoperability:&lt;/strong&gt; Frontend and backend don’t need to guess what a field means.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainability:&lt;/strong&gt; Less &lt;code&gt;if/else&lt;/code&gt; spaghetti to handle edge cases.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging:&lt;/strong&gt; Issues are easier to track when the data shape is predictable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Real-world example:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Date and Time: the backend sometimes stored dates in local time while the frontend assumed UTC. Users in different regions saw reports shift by a day.&lt;/li&gt;
&lt;li&gt;An endpoint returned &lt;code&gt;null&lt;/code&gt; for items, which immediately broke the UI. After we introduced clear standards, these problems disappeared.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Common Data Standards
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Date and Time: The Timezone Trap
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Always store in &lt;strong&gt;UTC&lt;/strong&gt; on the server.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;ISO 8601 format&lt;/strong&gt; (&lt;code&gt;2025-09-18T23:59:59Z&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: You can also store dates as Unix timestamps (milliseconds since epoch). They’re compact and avoid parsing issues across environments. ISO strings are easier to read and debug. Pick one format and apply it consistently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Let the &lt;strong&gt;frontend localize&lt;/strong&gt; for display.&lt;/li&gt;
&lt;li&gt;For ranges, store &lt;strong&gt;EOD (End of Day)&lt;/strong&gt; as &lt;code&gt;23:59:59&lt;/code&gt; and define the expected timezone.&lt;/li&gt;
&lt;li&gt;For recurring events, store &lt;strong&gt;timezone metadata&lt;/strong&gt; separately.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"start_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-09-01T00:00:00Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"end_date"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2025-09-30T23:59:59Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timezone"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Asia/Jakarta"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend (Node.js):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;endDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-09-30&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;endDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setUTCHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;59&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;end_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;endDate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend (React):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dayjs&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dayjs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;dayjs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;end_date&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MMM d, yyyy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  2. Boolean: When &lt;code&gt;null&lt;/code&gt; Breaks Logic
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Client: default to treat &lt;code&gt;null&lt;/code&gt; as &lt;strong&gt;false,&lt;/strong&gt; unless there are special cases or agreements.&lt;/li&gt;
&lt;li&gt;If true/false isn’t enough, use &lt;strong&gt;enums&lt;/strong&gt; (e.g., &lt;code&gt;pending&lt;/code&gt;, &lt;code&gt;approved&lt;/code&gt;, &lt;code&gt;rejected&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Document when a flag means &lt;strong&gt;state&lt;/strong&gt; vs. &lt;strong&gt;capability&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"is_admin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"approved"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend (Express):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isVerified&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isVerified&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;isVerified&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isVerified&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Dashboard&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VerifyScreen&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. Arrays: Empty or Null? Pick One.
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Default to return &lt;strong&gt;[]&lt;/strong&gt; instead of &lt;code&gt;null&lt;/code&gt; for empty array. Either as API response standard or the frontend always anticipate by defining fallbacks.&lt;/li&gt;
&lt;li&gt;Keep array item types consistent (IDs, objects, not both).&lt;/li&gt;
&lt;li&gt;Clarify whether &lt;strong&gt;ordering&lt;/strong&gt; is backend or frontend responsibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"roles"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"editor"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend (Node.js):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItems&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend (React):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fetchedItems&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;)&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;No items found&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;In TypeScript or modern JS, you can simplify null handling with operators like &lt;code&gt;??&lt;/code&gt; or &lt;code&gt;?..&lt;/code&gt; For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;array&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="p"&gt;[]).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;safeValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;isVerified&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dorey.github.io/JavaScript-Equality-Table/" rel="noopener noreferrer"&gt;https://dorey.github.io/JavaScript-Equality-Table/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  4. Numbers: Precision Matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Store numeric values as &lt;strong&gt;numbers&lt;/strong&gt;, not strings.&lt;/li&gt;
&lt;li&gt;For money, use &lt;strong&gt;integers in smallest units&lt;/strong&gt; (e.g., cents).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Important: Avoid doing critical math in the browser — floating point quirks can cause rounding errors. Always let the backend be the source of truth for final financial calculations, and use the frontend mainly for display/formatting. If you must calculate in the browser, add reverse checks (e.g., verify &lt;code&gt;a * b / b === a&lt;/code&gt;) to detect anomalies.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;For precision (e.g., FX rates), use &lt;strong&gt;decimal strings&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Define min/max limits (e.g., percentages 0–100).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;4999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"exchange_rate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.2345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"discount_percent"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;priceInCents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123e4567-e89b-12d3-a456-426614174000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;nstr&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;nstr&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nstr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;priceInCents&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxDecimals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// "9.99"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Strings: Keep Them Clean
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Trim whitespace before saving.&lt;/li&gt;
&lt;li&gt;Normalize casing where relevant (emails always lowercase).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Trimming and lowercasing help, but watch out for homoglyphs and invisible Unicode characters (e.g., &lt;a href="mailto:user@example.com"&gt;user@example.com&lt;/a&gt; vs uѕ&lt;a href="mailto:er@example.com"&gt;er@example.com&lt;/a&gt; with a Cyrillic “s”). For sensitive fields like emails and usernames, use String.prototype.normalize() or dedicated libraries to ensure consistency and security.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Use enums if the value should be constrained.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Nathan"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nathan@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"active"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleEmailChange&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. IDs and References: Don’t Mix Types
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;UUIDs&lt;/strong&gt; or consistent numeric IDs.&lt;/li&gt;
&lt;li&gt;Don’t mix &lt;code&gt;"123"&lt;/code&gt; and &lt;code&gt;123&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Clarify whether IDs are internal-only or public-facing.&lt;/li&gt;
&lt;li&gt;Prefer referencing by ID over embedding full objects unless necessary.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"user_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a12b3c4d-5678-90ef-1234-567890abcdef"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"order_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;uuidv4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`/api/users/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Error Handling: Predictable Responses
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Standardize errors with:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;code&lt;/code&gt;: machine-readable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;message&lt;/code&gt;: human-readable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;details&lt;/code&gt;: optional per-field errors&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Align with proper &lt;strong&gt;HTTP status codes&lt;/strong&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example JSON:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"INVALID_INPUT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Email is not valid"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid format"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Backend (Express):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;code&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INVALID_INPUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email is not valid&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;details&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Frontend:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;INVALID_INPUT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;showToast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;Adopting standards isn’t about adding complexities—it’s about saving time. A few practical tips:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Document early.&lt;/strong&gt; Put conventions in README or API docs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validate automatically.&lt;/strong&gt; Use Zod, Yup, or backend schema validators.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enforce in code.&lt;/strong&gt; Add type-checking and linter rules.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review often.&lt;/strong&gt; Check API responses during code reviews.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These standards are small investments that pay off in stability and predictability.&lt;/p&gt;




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

&lt;p&gt;Data standards aren’t glamorous, but they are the foundation of reliable systems. Whether it’s dates in UTC, booleans defaulting to false, or defaulting array values to empty array instead of null, these rules remove ambiguity and friction. The result: faster development, fewer bugs, and happier teams.&lt;/p&gt;

&lt;p&gt;Start small. Document the basics. Enforce them in code. Over time, it’ll feel natural—and you’ll wonder how you ever worked without them.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>data</category>
      <category>api</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Force Lodash Import Scope</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Fri, 30 Jun 2023 12:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/force-lodash-import-scope-3mo7</link>
      <guid>https://forem.com/agustinusnathaniel/force-lodash-import-scope-3mo7</guid>
      <description>&lt;h2&gt;
  
  
  Importing Lodash
&lt;/h2&gt;

&lt;p&gt;There are multiple ways to import lodash utilities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// whole import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// _.debounce();&lt;/span&gt;

&lt;span class="c1"&gt;// curly bracket / named import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// one-by-one / modules / single method import&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;debounce&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash/debounce&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One of the recommended way to import it is &lt;code&gt;one-by-one&lt;/code&gt; or modules or &lt;code&gt;single method&lt;/code&gt; import as it will produces smallest bundle size.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enforce Lodash Import Method
&lt;/h2&gt;

&lt;p&gt;How about if our project is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;maintained by multiple people, or&lt;/li&gt;
&lt;li&gt;we have various parts in our code which utilize lodash&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;and we want to enforce specific way to import lodash methods?&lt;/p&gt;

&lt;p&gt;We can use eslint and there's an eslint plugin for it: &lt;a href="https://www.npmjs.com/package/eslint-plugin-lodash" rel="noopener noreferrer"&gt;&lt;code&gt;eslint-plugin-lodash&lt;/code&gt;&lt;/a&gt;. This plugin has a rule named &lt;code&gt;import-scope&lt;/code&gt;. Here's how to configure it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm i &lt;span class="nt"&gt;-D&lt;/span&gt; eslint eslint-plugin-lodash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// .eslintrc.js&lt;/span&gt;
&lt;span class="cm"&gt;/** @type {import('eslint').Linter.Config} */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash/import-scope&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;method&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="cm"&gt;/** 'method' | 'member' | 'full' | 'method-package' */&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this configuration, eslint will warns us when we import lodash methods using other than the preferred import scope.&lt;/p&gt;

&lt;h3&gt;
  
  
  References:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.blazemeter.com/blog/import-lodash-libraries" rel="noopener noreferrer"&gt;https://www.blazemeter.com/blog/import-lodash-libraries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/wix-incubator/eslint-plugin-lodash/blob/master/docs/rules/import-scope.md" rel="noopener noreferrer"&gt;https://github.com/wix-incubator/eslint-plugin-lodash/blob/master/docs/rules/import-scope.md&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>lodash</category>
      <category>eslint</category>
      <category>sznmnotes</category>
    </item>
    <item>
      <title>Show Installed Global Node Deps</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Fri, 10 Feb 2023 00:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/show-installed-global-node-deps-2l0f</link>
      <guid>https://forem.com/agustinusnathaniel/show-installed-global-node-deps-2l0f</guid>
      <description>&lt;p&gt;To check node dependencies which are installed globally.&lt;/p&gt;

&lt;h2&gt;
  
  
  pnpm
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm list &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt; 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;references:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://pnpm.io/pnpm-cli" rel="noopener noreferrer"&gt;https://pnpm.io/pnpm-cli&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pnpm.io/cli/list#--global--g" rel="noopener noreferrer"&gt;https://pnpm.io/cli/list#--global--g&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  npm
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm list &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;--depth&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;references: &lt;a href="https://docs.npmjs.com/cli/v9/commands/npm-ls#global" rel="noopener noreferrer"&gt;https://docs.npmjs.com/cli/v9/commands/npm-ls#global&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Yarn
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn global list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;references: &lt;a href="https://classic.yarnpkg.com/en/docs/cli/global" rel="noopener noreferrer"&gt;https://classic.yarnpkg.com/en/docs/cli/global&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>pnpm</category>
      <category>npm</category>
      <category>yarn</category>
    </item>
    <item>
      <title>How to Secure Your Firebase Project</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Thu, 13 May 2021 00:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/how-to-secure-your-firebase-project-10lb</link>
      <guid>https://forem.com/agustinusnathaniel/how-to-secure-your-firebase-project-10lb</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;disclaimer: This is not by any means to be the best practice guide of using firebase in every project. Every project has its own needs and specifications. This guide may not be suitable with your needs.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Do you develop apps using Firebase? If so, we should know that our firebase configs will be exposed to the client (especially for web clients). Then what's the deal? Many possibilities can be happened and prevented. So many articles already covers on how to secure our firebase project by using security rules, authentication check, etc. I usually define my security rules using a package named &lt;a href="https://www.npmjs.com/package/@jahed/firebase-rules" rel="noopener noreferrer"&gt;&lt;code&gt;@jahed/firebase-rules&lt;/code&gt;&lt;/a&gt;. But how about preventing someone from making their own client and use our exposed firebase configs to do some shady stuffs towards our realtime database?&lt;/p&gt;

&lt;h2&gt;
  
  
  Apply Restrictions to the API Key
&lt;/h2&gt;

&lt;p&gt;Every firebase project is a GCP (Google Cloud Platform) project, so we can go to Google Cloud console to configure further our firebase project. We can restrict the API key even though it is exposed. Just follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to &lt;a href="https://console.cloud.google.com/apis" rel="noopener noreferrer"&gt;https://console.cloud.google.com/apis&lt;/a&gt;, &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45lojxd6svvojvo0g446.png" alt="Credentials Tab" width="800" height="515"&gt;
&lt;/li&gt;
&lt;li&gt;Select the desired firebase project,&lt;/li&gt;
&lt;li&gt;Go to &lt;code&gt;Credentials&lt;/code&gt; menu,&lt;/li&gt;
&lt;li&gt;Look for &lt;code&gt;API Keys&lt;/code&gt; section, select the API key which has &lt;code&gt;...(auto created by Firebase)&lt;/code&gt;, &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F41ysssju1soybymjzncn.png" alt="API Key Restrictions Tab" width="800" height="1038"&gt;
&lt;/li&gt;
&lt;li&gt;You will be directed to a page called &lt;code&gt;Restrict and rename API key&lt;/code&gt;, go to &lt;code&gt;Application restrictions&lt;/code&gt; section, select &lt;code&gt;HTTP referrers (web sites)&lt;/code&gt;, add your production web client domain in &lt;code&gt;Website restrictions&lt;/code&gt; section. Don't add &lt;code&gt;localhost&lt;/code&gt; if you intend to use the firebase project for production.&lt;/li&gt;
&lt;li&gt;Hit the &lt;code&gt;save&lt;/code&gt; button apply changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Voila! Your API key already restricted!&lt;/p&gt;

&lt;h2&gt;
  
  
  Use Different Firebase Project for Local Development
&lt;/h2&gt;

&lt;p&gt;Now that we already apply restrictions to our API key, how about our local development (&lt;code&gt;localhost&lt;/code&gt;)? Well, we can just create a new firebase project which will be used for your local development.&lt;/p&gt;

&lt;h3&gt;
  
  
  Special Case: Develop Hybrid Mobile App Using Ionic
&lt;/h3&gt;

&lt;p&gt;When developing hybrid app using ionic, chances are we gonna use the same firebase configs for Web and Android / iOS build (firebase web configs). Even though we made several firebase web configs, the API key will remain the same. If we apply restrictions to the API key to be only accessible for certain domain, then the API key won't be usable for the mobile build. Fortunately, there is a workaround for this. We can just create a new API key which don't have any domain restrictions just for our mobile build (the API key can't be easily accessed by the user anyway for Android / iOS build). So, we can use the same firebase configs for our web and mobile builds, but having different API key. Just go to &lt;a href="https://console.cloud.google.com/apis/credentials" rel="noopener noreferrer"&gt;https://console.cloud.google.com/apis/credentials&lt;/a&gt; (make sure you already select the corresponding project first), then just create a new API key which will be used for your mobile apps.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Defining Firebase security rules

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@jahed/firebase-rules" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@jahed/firebase-rules&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Restrict API key

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@devesu/how-to-secure-your-firebase-project-even-when-your-api-key-is-publicly-available-a462a2a58843" rel="noopener noreferrer"&gt;https://medium.com/@devesu/how-to-secure-your-firebase-project-even-when-your-api-key-is-publicly-available-a462a2a58843&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>firebase</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>2020 Recap</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Thu, 31 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/2020-recap-5fii</link>
      <guid>https://forem.com/agustinusnathaniel/2020-recap-5fii</guid>
      <description>&lt;p&gt;I think most would say 2020 is a roller coaster year. Even though, 2020 is one of a hella ride and worth for me to be remembered. I've made a quick recap in &lt;strong&gt;&lt;a href="https://sznm.dev/blog/2019-12-31-2019-recap" rel="noopener noreferrer"&gt;2019&lt;/a&gt;&lt;/strong&gt; before. So let's review what I've been up to in 2020.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned and Discovered Throughout the Year
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Instagram Filter Creation
&lt;/h3&gt;

&lt;p&gt;I love to edit photos and playing around with colors. Then I learn how to make my own Instagram filter. Turned out facebook provides a tool dedicated for that called Spark AR Studio. You can make literally almost anything starting from a color filter, games, quizzes, anything you can think of when playing around with AR. I just want to edit colors so I just go with color filter. Making a color filter is quite simple, you only have to create a LUT, import it to your Spark AR projects as a layer, export it, and upload it to Spark AR and connect it with your instagram account. Check out some filter I made &lt;a href="https://www.instagram.com/agustinusnathaniel/" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  React Hooks and Context
&lt;/h3&gt;

&lt;p&gt;React hooks been around but I've never learn or use it until I finally get my hands on it when building  an app as part of my undergraduate thesis. I used to stick with Classful Components and use redux (just in desperate moments), but learning hooks and context helps me to switch fully to Functional Components.&lt;/p&gt;

&lt;h3&gt;
  
  
  TailwindCSS
&lt;/h3&gt;

&lt;p&gt;2019 was a year where I finally get my hands on React and started to re-learn GatsbyJS (I tried to learn Gatsby before touching React at 2018 and it was a disaster idea). It was a really fun journey. But there's something I've been wanting to try but never accomplished at 2019....TailwindCSS. So, at 2020 I finally get my hands on it and it was really different than other styling frameworks I've ever used before (Bootstrap, Bulma, SemanticUI). I use it to rebuild my first personal site (agustinusnathaniel.com). But around the end of 2020 I re-write it again using Chakra-UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Svelte
&lt;/h3&gt;

&lt;p&gt;I heard svelte often but never tried it until I stumble upon Rich Harris's video when he explain about reactivity. His explanation is really great and quite eye-opening for me who mainly develop using React. So I tried it, made some little projects with it and I like how straightforward it is. I'd recommend using svelte if you are looking for a powerful javascript libraries like react or vue (framework) but with an easier learning curve. I'd love to see Svelte's development over the next years. But not long after that I also got my hands on Next.js.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js
&lt;/h3&gt;

&lt;p&gt;I learn Next.js and implement it in some of my projects. I used to avoid learning and using Next.js because I haven't understand SSR properly yet. I thought Next.js is only used for SSR projects. Then I found out Next.js support both static site generation and server side rendering since version 9.3 and I got my hands on it for the first time not so long after version 9.4 released. Since then, Next.js has been my go-to react framework for most of my projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  Chakra UI
&lt;/h3&gt;

&lt;p&gt;I tried several design system or component libraries this year: Fluent UI, Carbon, Material UI, and Chakra UI. After trying out and play around with those design systems, I found out Chakra UI suits my needs the best. It's easy to be configured, easy to use, and it has color mode styling and management built in. After some time, I made a template with Next.js, Chakra UI, and TypeScript pre-configured and has been my go-to template to initiate most of my recent projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Projects I Worked on in 2020
&lt;/h2&gt;

&lt;p&gt;most of the projects are just a little side projects for me to try implement some simple ideas or learning something.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Personal Site Revamp
&lt;/h3&gt;

&lt;p&gt;I re-write my personal site &lt;a href="https://agustinusnathaniel.com" rel="noopener noreferrer"&gt;agustinusnathaniel.com&lt;/a&gt; using TailwindCSS and add blog section. At the end of the year I replace the TailwindCSS usage with Chakra UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Le-Cook
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://le-cook.sznm.dev" rel="noopener noreferrer"&gt;app&lt;/a&gt; to find food recipe, powered by RecipePuppy API&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Covid-19 Data
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://covid19.sznm.dev" rel="noopener noreferrer"&gt;Monitor Covid-19 statistics&lt;/a&gt;, powered by &lt;a class="mentioned-user" href="https://dev.to/mathdroid"&gt;@mathdroid&lt;/a&gt;'s Covid-19 API, &lt;a class="mentioned-user" href="https://dev.to/ariya"&gt;@ariya&lt;/a&gt;'s Dekontaminasi API, and @Reynaldi531's api-covid19-indonesia v2. First developed using Gatsby, then re-wrote it with Next.js and Chakra UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Advice Generator
&lt;/h3&gt;

&lt;p&gt;A random &lt;a href="https://advicegen.sznm.dev" rel="noopener noreferrer"&gt;advice generator&lt;/a&gt; powered by Advice Slip JSON API, written using Svelte.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Insta Profile
&lt;/h3&gt;

&lt;p&gt;A simple Svelte &lt;a href="https://instaprofile.sznm.dev" rel="noopener noreferrer"&gt;app&lt;/a&gt; fetching data from Instagram.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. sznm.dev
&lt;/h3&gt;

&lt;p&gt;I make &lt;a href="https://sznm.dev" rel="noopener noreferrer"&gt;another personal site&lt;/a&gt; of myself dedicated for dev content, built using Next.js, composed using Chakra UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. KapturaLumina
&lt;/h3&gt;

&lt;p&gt;Basic Photography Learning Mobile App with Gamification. Built using Ionic, React, and Firebase. Available as &lt;a href="https://kapturalumina.sznm.dev" rel="noopener noreferrer"&gt;PWA&lt;/a&gt; and &lt;a href="https://play.google.com/store/apps/details?id=dev.sznm.kapturalumina" rel="noopener noreferrer"&gt;android&lt;/a&gt; app. I built it as part of my undergraduate thesis.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. NextChakra-Starter
&lt;/h3&gt;

&lt;p&gt;A &lt;a href="https://nextchakra-starter.sznm.dev/" rel="noopener noreferrer"&gt;template&lt;/a&gt; I made to initialize Next.js projects with TypeScript and Chakra UI setup. Most of my following projects are initalized / generated using this template.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. Add to Calendar Generator
&lt;/h3&gt;

&lt;p&gt;A web &lt;a href="https://addtocal.sznm.dev" rel="noopener noreferrer"&gt;app&lt;/a&gt; to generate Add to Calendar link (Google Calendar).&lt;/p&gt;

&lt;h3&gt;
  
  
  10. InstaDLD
&lt;/h3&gt;

&lt;p&gt;Instagram post media &lt;a href="https://instadld.sznm.dev" rel="noopener noreferrer"&gt;downloader&lt;/a&gt; with multipost download supported.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. Public APIs
&lt;/h3&gt;

&lt;p&gt;An &lt;a href="https://publicapis.sznm.dev" rel="noopener noreferrer"&gt;app&lt;/a&gt; to find public API for you next projects. Didn't thought it could be the &lt;a href="https://www.producthunt.com/posts/public-apis-3" rel="noopener noreferrer"&gt;product of the day&lt;/a&gt; in Product Hunt at 26 Dec 2020. Powered by api.publicapis.org.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some other stuffs worth to mention
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;I graduated from college (finally) 😊😊. Really grateful for everyone who supported me until now, especially my family and friends. &lt;/li&gt;
&lt;li&gt;I started my career as Software Engineer right after I finished my thesis. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All in all, I'm glad I can get through 2020 pretty well enough. I never imagined I'd graduate in time, especially when the pandemic situation started to happen and my thesis proposals got rejected several times. Hoping to grow more in 2021 and crafting greater stuffs. Thank you for reading and I hope you are doing well 😄.&lt;/p&gt;

&lt;p&gt;If you're interested in another version of my review of my journey in 2020, I also published it at &lt;a href="https://twitter.com/sozonome/status/1345261139358142467" rel="noopener noreferrer"&gt;twitter&lt;/a&gt; (focused around the projects I made throughout the year).&lt;/p&gt;

&lt;p&gt;My previous recap: &lt;strong&gt;&lt;a href="https://sznm.dev/blog/2019-12-31-2019-recap" rel="noopener noreferrer"&gt;2019&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>2020</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Monitor and Measure Site Performance from Time to Time and Automatically using Speedlify</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Sat, 07 Nov 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/monitor-and-measure-site-performance-from-time-to-time-and-automatically-using-speedlify-4inm</link>
      <guid>https://forem.com/agustinusnathaniel/monitor-and-measure-site-performance-from-time-to-time-and-automatically-using-speedlify-4inm</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Want to measure and monitor your site performance from time to time and automatically? Deploy &lt;a href="https://speedlify.dev" rel="noopener noreferrer"&gt;Speedlify&lt;/a&gt; through &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; and use &lt;a href="https://github.com/features/actions" rel="noopener noreferrer"&gt;Github Actions&lt;/a&gt; (cron schedule) or Zapier to automatically trigger build every desired time.&lt;/p&gt;

&lt;p&gt;You can directly visit &lt;a href="https://github.com/zachleat/speedlify/#deploy-to-netlify" rel="noopener noreferrer"&gt;this page&lt;/a&gt; if you know what to do next. But if you need some guidance, this article will help you step by step.&lt;/p&gt;

&lt;h3&gt;
  
  
  What will be covered in this article?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;How to configure Speedlify&lt;/li&gt;
&lt;li&gt;How to deploy Speedlify&lt;/li&gt;
&lt;li&gt;Using Netlify build hooks and Github Actions to automatically trigger build Speedlify page every desired time.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  This article won't cover...
&lt;/h3&gt;

&lt;p&gt;SEO or visitor related metrics like Google Analytics. The site performance mentioned in this article is &lt;a href="https://developers.google.com/web/tools/lighthouse" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; based performance measurement.&lt;/p&gt;




&lt;h2&gt;
  
  
  Measuring Site Performance
&lt;/h2&gt;

&lt;p&gt;Nowadays there are so many ways to measure site performance beside running lighthouse in your local machine (Chrome Dev Tools). Even recently &lt;a href="https://vercel.com" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt;, PaaS for frontend deployment released a feature called &lt;a href="https://vercel.com/docs/analytics/overview.amp" rel="noopener noreferrer"&gt;Analytics&lt;/a&gt;. This feature can show your &lt;a href="https://nextjs.org/analytics" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; or &lt;a href="https://vercel.com/blog/gatsby-analytics" rel="noopener noreferrer"&gt;Gatsby&lt;/a&gt; site performance automatically from time-to-time without having to configure anything (&lt;em&gt;almost zero-config&lt;/em&gt;). But this feature is limited to be applicable for one project if your vercel account is a free version.&lt;/p&gt;

&lt;p&gt;What if we have more than one project / site to be measured from time-to-time without spending extra cost? Well, this is where &lt;a href="https://speedlify.dev" rel="noopener noreferrer"&gt;Speedlify&lt;/a&gt; comes in, a template for site performance monitor created by &lt;a href="https://github.com/zachleat" rel="noopener noreferrer"&gt;Zach Leatherman&lt;/a&gt;. Speedlify built using a static site generator framework called &lt;a href="https://www.11ty.dev/" rel="noopener noreferrer"&gt;11ty(eleventy)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I've tried to deploy my own speedlify &lt;a href="https://audit.sznm.dev" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;fun fact: I found Speedlify when I was just randomly exploring 11ty docs page (not really important, just intermezzo 😄)&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Well, let's get our hands dirty!&lt;/p&gt;

&lt;h2&gt;
  
  
  How?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is needed?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;a Github account&lt;/li&gt;
&lt;li&gt;a Netlify account&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  This Guide was Written with Assumtions that You:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;know how to use basic Git commands&lt;/li&gt;
&lt;li&gt;familiar with Netlify&lt;/li&gt;
&lt;li&gt;familiar with Node.js environment (installed node and npm)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I suggest you to visit &lt;a href="https://speedlify.dev" rel="noopener noreferrer"&gt;Speedlify&lt;/a&gt; to get some glimpse on what we will be using. There's a link to the source code repository on that page which shows you how to deploy your own Speedlify page. However, if you are having some difficulties, you can folow these steps:&lt;/p&gt;

&lt;h3&gt;
  
  
  #1: Clone Speedlify Repo
&lt;/h3&gt;

&lt;p&gt;Import speedlify repository to your github account (&lt;a href="https://github.com/new/import" rel="noopener noreferrer"&gt;https://github.com/new/import&lt;/a&gt;), input this URL: &lt;code&gt;https://github.com/zachleat/speedlify/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;After the import process is done, clone your speedlify repo into your local machine or just run these command below in your local folder:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/[YOUR_GITHUB_USERNAME]/speedlify/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd speedlify
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run &lt;code&gt;npm install&lt;/code&gt; or &lt;code&gt;npm i&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  #2: Configure URLs
&lt;/h3&gt;

&lt;p&gt;Open &lt;code&gt;_data/sites&lt;/code&gt; folder. Every file you create here will represent a category. You can defined more than one URL for every category. Just delete all default files in &lt;code&gt;_data/sites&lt;/code&gt;. Create a file &lt;code&gt;[CATEGORY_NAME].js&lt;/code&gt;. (change [CATEGORY_NAME] with your desired category name). You can create more than one category, but you must know some limitations here: &lt;a href="https://github.com/zachleat/speedlify/#known-limitations" rel="noopener noreferrer"&gt;https://github.com/zachleat/speedlify/#known-limitations&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// _data/sites/[CATEGORY_NAME].js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Category Name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// optional, falls back to object key&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Category Description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;frequency&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 23 hours&lt;/span&gt;

        &lt;span class="c1"&gt;// Use "run" if the sites don’t share assets on the same origin&lt;/span&gt;
        &lt;span class="c1"&gt;// and we can reset chrome with each run instead of&lt;/span&gt;
        &lt;span class="c1"&gt;// each site in every run (it’s faster)&lt;/span&gt;
        &lt;span class="c1"&gt;// Use "site" if sites are all on the same origin and share assets.&lt;/span&gt;
        &lt;span class="na"&gt;freshChrome&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;run&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;urls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://[YOUR_SITE_URL]/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://[YOUR_SITE_URL]/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// etc&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

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

&lt;/div&gt;



&lt;h4&gt;
  
  
  explanations
&lt;/h4&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;frequency&lt;/code&gt;: to set minimum time needed before next measurement. If we set &lt;code&gt;60*23&lt;/code&gt; (1380 minutes or 23 hours) it means we will be measuring our site performance once every 23 hours. This value will be used to avoid measurement more than once before the minimum time was passed which will affect the build time. If we set the frequency to 23 hours and trigger build every 6 hours, the measurement for this category will be skipped if the last measurement haven't passed 23 hours.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  #3: Test run in local
&lt;/h3&gt;

&lt;p&gt;Run &lt;code&gt;npm run start&lt;/code&gt;. If the categories you input are shown, you can continue to the next step. You won't see any measurements. Measurements will be done at the build time when we deploy your Speedlify to Netlify. Commit your changes and run &lt;code&gt;git push&lt;/code&gt; to apply changes to your github repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  #4: Deploy configured Speedlify through Netlify
&lt;/h3&gt;

&lt;p&gt;Open your Netlify dashboard (&lt;a href="https://app.netlify.com/" rel="noopener noreferrer"&gt;https://app.netlify.com/&lt;/a&gt;), click "New site from Git". Point it to your Speedlify repository.&lt;/p&gt;

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

&lt;p&gt;Confirm the build configurations until "Deploy Site" button is shown and click that button. Netlify build will do the build and deployment process. If the build and deployment process are successful, you can preview your deployment.&lt;/p&gt;

&lt;p&gt;The measurement page will look like this:&lt;/p&gt;

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

&lt;h3&gt;
  
  
  #5: Configure Github Actions to Automatically Trigger Build Every Desired Time
&lt;/h3&gt;

&lt;p&gt;To do measurement from time-to-time, we will utilize Netlify build hooks and Github Actions. Why? Because measurement are only done on build time. It will be a hassle to trigger the build process manually. You don't have to use Github Actions if you prefer to use Zapier or similar services which support cron schedule. The same process can be achieved using Zapier with Schedule by Zapier and Webhooks by Zapier. However for the time being Webhooks by Zapier can only be enabled if you are a Zapier premium user. Therefore, in this guide I will use Github Actions as an alternative which are more friendly to our pocket.&lt;/p&gt;

&lt;p&gt;We will need build hook link (webhooks) to trigger build in Netlify. To get that link, open your speedlify project in your Netlify dashboard, then open "Site settings". Open "Build &amp;amp; Deploy".&lt;/p&gt;

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

&lt;p&gt;Then point to "Build hooks", click "Add build hook". &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F095y1oi3jpqxqlg56b3w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F095y1oi3jpqxqlg56b3w.png" alt="Add Build Hook" width="800" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We will get the build hooks URL, copy that link.&lt;/p&gt;

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

&lt;p&gt;Now we can configure Github Actions to automatically trigger build to Netlify. Go back to your speedlify local folder, add a file named &lt;code&gt;.github/workflows/main.yml&lt;/code&gt; and paste the copied build hooks URL into this file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .github/workflows/main.yml&lt;/span&gt;

&lt;span class="c1"&gt;# edit according to your needs&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Trigger Netlify Build daily on Weekday&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="c1"&gt;# if you want to define your own build trigger schedule, just change the cron schedule value below&lt;/span&gt;
    &lt;span class="c1"&gt;# use https://crontab.guru/ if you are having some difficulties on how to define the cron values&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;22&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;MON-FRI'&lt;/span&gt;
    &lt;span class="c1"&gt;# every day on weekdays 22:00.&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Netlify build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Curl request&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;curl -X POST -d {} YOUR_BUILD_HOOK_URL&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;Commit your changes and run &lt;code&gt;git push&lt;/code&gt;. If it's configured correctly, it will be shown in "Actions" tab at your github repository.&lt;/p&gt;

&lt;p&gt;Now you have a dedicated page to monitor your site performance from time-to-time and automatically updated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;It's important to note that the more URL you add to your speedlify configurations, the build time needed will be increased. The free version of Netlify have a maximum 15 minutes build time for every build process and 300 minutes build time quota for every month.&lt;/p&gt;

&lt;p&gt;I work around it by limiting my measurement to be done maximum once every 23 hours and limiting the total URLs from all category to be around 5 until 10, and automatically trigger the build every day on weekdays only at 10PM. With those configurations, every build would spend around 4 until 7 minutes (&amp;lt;15 minutes), so I won't hit the monthly build time quota limit (4.5 * 5 * 7 ~= 160 minutes -&amp;gt; &amp;lt;300 minutes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Thank You!
&lt;/h2&gt;

&lt;p&gt;For reading this article. I hope you found this useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.speedlify.dev/" rel="noopener noreferrer"&gt;https://www.speedlify.dev/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zachleat/speedlify/#deploy-to-netlify" rel="noopener noreferrer"&gt;https://github.com/zachleat/speedlify/#deploy-to-netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zachleat/speedlify/#known-limitations" rel="noopener noreferrer"&gt;https://github.com/zachleat/speedlify/#known-limitations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.zachleat.com/web/speedlify/" rel="noopener noreferrer"&gt;https://www.zachleat.com/web/speedlify/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Some Alternatives
&lt;/h2&gt;

&lt;p&gt;(some exhaustive list of other tools to measure your website performance)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://web.dev/measure/" rel="noopener noreferrer"&gt;https://web.dev/measure/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web.dev/measure/" rel="noopener noreferrer"&gt;https://www.lightest.app/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Discussions
&lt;/h2&gt;

&lt;p&gt;How important it is to measure site performance for you? How often do you measure your site performance? Which tools do you usually use?&lt;/p&gt;

</description>
      <category>lighthouse</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>May 2020 Quarantine Self Challenge</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Sat, 02 May 2020 00:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/may-2020-quarantine-self-challenge-32ei</link>
      <guid>https://forem.com/agustinusnathaniel/may-2020-quarantine-self-challenge-32ei</guid>
      <description>&lt;p&gt;Recently I challenged myself to make a web app as soon as possible in two days. Managed to make two. One is a simple Food Recipe App and the other is COVID-19 Data App. Turned out it was so fun.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Le Cook
&lt;/h2&gt;

&lt;p&gt;Fun food recipe catalog app powered by &lt;a href="https://recipepuppy.com" rel="noopener noreferrer"&gt;RecipePuppy&lt;/a&gt; API....&lt;/p&gt;

&lt;p&gt;&lt;a href="https://le-cook.now.sh/" rel="noopener noreferrer"&gt;https://le-cook.now.sh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fns0syiyxq6ml6woqgbx5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fns0syiyxq6ml6woqgbx5.jpg" alt="Le Cook (1)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozvxgg40tky44keufpyn.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fozvxgg40tky44keufpyn.jpg" alt="Le Cook (2)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftse6p2uqbf0ukvsccwxk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftse6p2uqbf0ukvsccwxk.jpg" alt="Le Cook (3)" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  2. COVID-19 Data
&lt;/h2&gt;

&lt;p&gt;presenting COVID-19 statistics powered by &lt;a href="https://github.com/mathdroid/covid-19-api/" rel="noopener noreferrer"&gt;@mathdroid&lt;/a&gt;'s covid-19-api. I made a vanilla JS version of this app several days ago and I decided to re-develop it using Gatsby (React).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://covid19data.now.sh/" rel="noopener noreferrer"&gt;https://covid19data.now.sh/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1krsw9ov3if4hcbxanh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq1krsw9ov3if4hcbxanh.jpg" alt="COVID-19 Data App (1)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flewkq8pmtp0xdfnnx5gr.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flewkq8pmtp0xdfnnx5gr.jpg" alt="COVID-19 Data App (2)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0kekmptnc3vqsa2obve.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0kekmptnc3vqsa2obve.jpg" alt="COVID-19 Data App (3)" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both &lt;a href="https://le-cook.now.sh/" rel="noopener noreferrer"&gt;Le Cook&lt;/a&gt; and &lt;a href="https://covid19data.now.sh/" rel="noopener noreferrer"&gt;COVID-19 Data&lt;/a&gt; app were developed using Segment's Evergreen UI. Just discovered this React UI Framework and turns out it's so convenient to implement it on these projects.&lt;/p&gt;

&lt;p&gt;When developing COVID-19 Data app, I realized Evergreen UI have no opinionated way to construct responsive layouts. So, I decided to combine it with TailwindCSS which I already tried to use at this personal site. It worked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yqjpwmiuhe0nuwo6124.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7yqjpwmiuhe0nuwo6124.jpg" alt="Evergreen UI (1)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5kjzqmcvgl0xb65nqhl.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5kjzqmcvgl0xb65nqhl.jpg" alt="Evergreen UI (2)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkes45ykybafvljtn211o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkes45ykybafvljtn211o.jpg" alt="Evergreen UI (3)" width="800" height="800"&gt;&lt;/a&gt; &lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2udocedfzvsmyn4lxdw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz2udocedfzvsmyn4lxdw.jpg" alt="Evergreen UI (4)" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I decided to deploy it using &lt;a href="https://vercel.com" rel="noopener noreferrer"&gt;Vercel&lt;/a&gt; and I'm so shocked by how fast it is compared to &lt;a href="https://netlify.com" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;. I'm considering to migrate to Vercel from Netlify for my previous projects, including this site.&lt;/p&gt;

&lt;p&gt;I posted my recent projects &lt;a href="https://sznm.dev/projects" rel="noopener noreferrer"&gt;here&lt;/a&gt;. You can access the project and the project's GitHub repository link from this &lt;a href="https://sznm.dev/projects" rel="noopener noreferrer"&gt;page&lt;/a&gt;.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stackbit, the Game Changer</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Sun, 22 Mar 2020 05:51:32 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/stackbit-the-game-changer-gc6</link>
      <guid>https://forem.com/agustinusnathaniel/stackbit-the-game-changer-gc6</guid>
      <description>&lt;p&gt;&lt;em&gt;by the time I wrote this post, Stackbit is still at Beta.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Have you ever thought of launching a landing page or a blog with the technology of your choice in just under 10 minutes? Since &lt;a href="https://jamstack.wtf/#meaning" rel="noopener noreferrer"&gt;JAMStack&lt;/a&gt; is getting more popular these days, more sites are being developed this way. Building it from scratch is also not too hard. But the problem is sometimes the initialization of the project or the deployment process can spend more time than you think. Not to mention if you are someone who just want to get a little grasp of what all this about without getting your hands dirty and spending some hours just to know what are you actually trying to do. Or if you are a first-timer who needs a step-by-step guide.&lt;/p&gt;

&lt;p&gt;Yes, this is for you if you want to get a taste of what is JAMStack site about, how does it work in general, but you don't want to build it from scratch just to know it. This is for you if you are someone who are very familiar with building JAMStack sites, having multiple and upcoming clients who requests high performance yet low-cost site. Well, this is where &lt;a href="https://www.stackbit.com/" rel="noopener noreferrer"&gt;Stackbit&lt;/a&gt; plays the role for you.&lt;/p&gt;

&lt;p&gt;You can call Stackbit as your smart-assistant who helps you to start your JAMStack site. It's very easy to use, you just need to prepare your &lt;a href="https://www.github.com" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; account (&lt;a href="https://gitlab.com" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; and &lt;a href="https://bitbucket.com" rel="noopener noreferrer"&gt;BitBucket&lt;/a&gt; support coming soon) and a &lt;a href="https://netlify.com" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; account. The next thing is just register yourself into Stackbit (you can even register using your GitHub account so it's more convenient) and you can start to Build a Project. You gonna choose the site theme, the site generator framework, CMS, then you can deploy it. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcnssp8ul8ti64bky4ftq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcnssp8ul8ti64bky4ftq.png" alt="Alt Text" width="800" height="568"&gt;&lt;/a&gt;&lt;br&gt;
All of it are done just by picking, doing some clicks, and voila. Yeah, all of the process involve no code. But if you want to develop and customize the project further, that's where your hands start getting dirty.&lt;/p&gt;

&lt;p&gt;So, what are you waiting for? Don't just read this post, it won't bring you anywhere. &lt;em&gt;&lt;a href="https://www.stackbit.com/" rel="noopener noreferrer"&gt;Try it now&lt;/a&gt;&lt;/em&gt; and you will know what I'm talking about 😂.&lt;/p&gt;

</description>
      <category>stackbit</category>
      <category>netlify</category>
      <category>github</category>
      <category>sanity</category>
    </item>
    <item>
      <title>2019 Recap</title>
      <dc:creator>Agustinus Nathaniel</dc:creator>
      <pubDate>Tue, 31 Dec 2019 00:00:00 +0000</pubDate>
      <link>https://forem.com/agustinusnathaniel/2019-recap-4of3</link>
      <guid>https://forem.com/agustinusnathaniel/2019-recap-4of3</guid>
      <description>&lt;p&gt;I spent most of my time in 2019 at college, trying to finish all my university courses by the end of 7th semester so I can focus on Thesis / Final Project at the final semester (8th semester). Planning to graduate in 2020. So...not really much happened in this year, but I learned so much in terms of mobile application and web development. How to build an Android App and got my hands on React framework for the first-time in this year.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned in 2019
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Java and XML - How to build Native Android apps using Android Studio.&lt;/li&gt;
&lt;li&gt;REST API - How to fetch data and display it with recycler view in Android app &lt;/li&gt;
&lt;li&gt;PHP - how to program a website with PHP, MVC.&lt;/li&gt;
&lt;li&gt;How to setup a website using database with PHP, laragon, and MySQL&lt;/li&gt;
&lt;li&gt;CodeIgniter - how to use CodeIgniter to build website.&lt;/li&gt;
&lt;li&gt;How to deploy PHP sites with its database to cPanel hosting.&lt;/li&gt;
&lt;li&gt;How to setup arcanist in Linux Ubuntu.&lt;/li&gt;
&lt;li&gt;How to use Git for managing personal and team projects.&lt;/li&gt;
&lt;li&gt;TypeScript - how to use it, what is the importance of strong typing, and how to compile it into JS&lt;/li&gt;
&lt;li&gt;React - how to build an app using React with CRA.&lt;/li&gt;
&lt;li&gt;GraphQL - what is it and how is it used&lt;/li&gt;
&lt;li&gt;Apollo - how to implement apollo as a client for GraphQL&lt;/li&gt;
&lt;li&gt;GatsbyJS - how to build a site with Gatsby&lt;/li&gt;
&lt;li&gt;Ionic - how to build hybrid apps and how does it different from native apps.&lt;/li&gt;
&lt;li&gt;Angular2+ - forced to learn this framework because learning Ionic Angular and luckily I've learned TypeScript.&lt;/li&gt;
&lt;li&gt;Zeit - found a Netlify alternative and how to deploy site with it.&lt;/li&gt;
&lt;/ul&gt;

</description>
    </item>
  </channel>
</rss>
