<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Orchard Lab</title>
    <link>https://orchardlab.dev/</link>
    <description>Recent content on Orchard Lab</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-us</language>
    <lastBuildDate>Sat, 04 Oct 2025 20:49:28 -0700</lastBuildDate><atom:link href="https://orchardlab.dev/feed.xml" rel="self" type="application/rss+xml" />
    
    <item>
      <title>Short Blog</title>
      <link>https://orchardlab.dev/posts/short-blog/</link>
      <pubDate>Sat, 04 Oct 2025 20:49:28 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/short-blog/</guid>
      <description>&lt;p&gt;It&amp;rsquo;s been 3 days not reading any news, no social media, using computer just for coding.&lt;/p&gt;
&lt;p&gt;Feels great.&lt;/p&gt;
&lt;p&gt;While reading my RSS feed today, I realized that you can just write short blog as well. Thanks &lt;a href=&#34;https://seths.blog/2025/10/reciprocity/&#34;&gt;Seth Godin&lt;/a&gt;!&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Ship It</title>
      <link>https://orchardlab.dev/posts/ship-it/</link>
      <pubDate>Sat, 22 Jun 2024 08:51:07 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/ship-it/</guid>
      <description>&lt;p&gt;I have a problem. A problem that has bothered me for a long time: not finishing the side projects I started.&lt;/p&gt;
&lt;p&gt;No matter how big or small the project is, I always manage to give up at the very last minute. Whether the excuse is chasing so-called perfection or finding it&amp;rsquo;s not useful for myself anymore. Not to mention you could use the fancy term &amp;ldquo;sunk cost&amp;rdquo; to justify it.&lt;/p&gt;
&lt;p&gt;I am sick of it.&lt;/p&gt;
&lt;p&gt;I need to make a change this year.&lt;/p&gt;
&lt;p&gt;Instead of letting my mind go wild and constantly running &lt;code&gt;npm init&lt;/code&gt; or &lt;code&gt;Xcode -&amp;gt; New&lt;/code&gt;, I need to finish the damn thing. I realized that not finishing the project causes more damage to my conscious mind than the &amp;ldquo;sunk cost.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;An unpopular finished project is fine, even if it&amp;rsquo;s not popular, even if it doesn&amp;rsquo;t resonate with other people as it once did with me.&lt;/p&gt;
&lt;p&gt;Like training for a marathon, some days you feel awful. You could choose to skip it or have a shorter run. Skipping is very contagious.&lt;/p&gt;
&lt;p&gt;Finishing a project needs practice, discipline, accepting imperfection, and prioritizing closure over lingering.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Multiple environments using Fly.io</title>
      <link>https://orchardlab.dev/posts/fly-envs/</link>
      <pubDate>Sat, 03 Feb 2024 15:01:57 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/fly-envs/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been using fly.io since 2021. But I never realize I can deploy multiple environments (&lt;code&gt;staging&lt;/code&gt;, &lt;code&gt;production&lt;/code&gt;) easily as well. In this post I will share how can you add a &lt;code&gt;staging&lt;/code&gt; environment in 5 minutes.&lt;/p&gt;
&lt;p&gt;Assuming you already have a &lt;code&gt;fly.toml&lt;/code&gt; in your project, something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;example-production&amp;#39;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;primary_region&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;lax&amp;#39;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;APP_ENV&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;production&amp;#39;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;AWS_REGION&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;us-west-1&amp;#39;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http_service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;internal_port&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3000&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;force_https&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;auto_stop_machines&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;auto_start_machines&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;min_machines_running&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;processes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;app&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;vm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;cpu_kind&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;shared&amp;#39;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;cpus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;memory_mb&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First let&amp;rsquo;s create a new config called &lt;code&gt;fly.staging.toml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;cp fly.toml fly.staging.toml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s modify the &lt;code&gt;fly.staging.toml&lt;/code&gt; to reflect what we want in this env:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;nx&#34;&gt;app&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;example-staging&amp;#34;&lt;/span&gt; &lt;span class=&#34;c&#34;&gt;# &amp;lt;--- Making sure you have a different name&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;primary_region&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;lax&amp;#34;&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;http_service&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;internal_port&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3000&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;force_https&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;auto_stop_machines&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;auto_start_machines&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;min_machines_running&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;processes&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;app&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;vm&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;cpu_kind&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;shared&amp;#34;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;cpus&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;memory_mb&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1024&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;APP_ENV&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;staging&amp;#34;&lt;/span&gt;  &lt;span class=&#34;c&#34;&gt;# &amp;lt;--- Here we set the APP_ENV to staging&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;AWS_REGION&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;us-west-1&amp;#39;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can&amp;rsquo;t use &lt;code&gt;fly launch&lt;/code&gt; though with this new config. As &lt;code&gt;fly launch&lt;/code&gt; only take &lt;code&gt;fly.toml&lt;/code&gt; as of today. Instead we would need to create a new app for now.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# making sure the name here is the same with the above fly.staging.toml app name&lt;/span&gt;
fly apps create example-staging
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now with the app created. All we need to do is to deploy using this new config:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;fly deploy --config ./fly.staging.toml
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Yay! You just deployed a staging env using the same repository. You can run &lt;code&gt;fly apps list&lt;/code&gt; to check the list of apps now.&lt;/p&gt;
&lt;h2 id=&#34;bonus-auto-deployment-using-gha&#34;&gt;Bonus (Auto deployment using GHA)&lt;/h2&gt;
&lt;p&gt;With this we can have a different Github workflow to deploy to &lt;code&gt;staging&lt;/code&gt; when pushes to &lt;code&gt;staging&lt;/code&gt; branch:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.github/workflows/fly-staging.yml&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Fly Deploy Staging&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;staging&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;deploy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Deploy app&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/checkout@v3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;superfly/flyctl-actions/setup-flyctl@master&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;flyctl deploy --remote-only --config ./fly.staging.toml&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;FLY_API_TOKEN&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ secrets.FLY_API_TOKEN }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Cheers and happy flying!&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>🎨 Extract dominant colors in Elixir</title>
      <link>https://orchardlab.dev/posts/dominant-colors/</link>
      <pubDate>Sun, 26 Nov 2023 10:52:27 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/dominant-colors/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;This is an article on how to create a Elixir wrapper of Rust crate. If you are more care about using the final library to extract dominant colors, head over to &lt;a href=&#34;https://github.com/29decibel/dominant_colors/&#34;&gt;29decibel/dominant_colors&lt;/a&gt; or reference this &lt;a href=&#34;https://gist.github.com/29decibel/845ced62bfbaa25cd5ed064e0bf3b6cf&#34;&gt;Livebook gist&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/dominant-colors/livebook.png&#34; alt=&#34;livebook extract dominant colors&#34;&gt;&lt;/p&gt;
&lt;p&gt;I found this very nice Rust library called &lt;a href=&#34;https://github.com/okaneco/kmeans-colors&#34;&gt;kmeans-colors&lt;/a&gt; which can extract dominant colors from a given image. It based on &lt;a href=&#34;https://en.wikipedia.org/wiki/K-means_clustering&#34;&gt;k means clustering&lt;/a&gt; algorithm. It even comes with a binary. Which you can generate color palette like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;kmeans_colors -i gfx/mountains.jpg --no-file --palette
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Command line is great, but I wish I can use this in Elixir and Livebook.&lt;/p&gt;
&lt;p&gt;So I go ahead wrote a simple wrapper on top of it using the awesome &lt;code&gt;rustler&lt;/code&gt; library. In this article, I am gonna document how you can leverage the great Rust ecosystem and bring them into Elixir.&lt;/p&gt;
&lt;h2 id=&#34;create-elixir-library&#34;&gt;Create Elixir library&lt;/h2&gt;
&lt;p&gt;Let&amp;rsquo;s create a new library using &lt;code&gt;mix new&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mix new dominant_colors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I choose to use &lt;code&gt;dominant_colors&lt;/code&gt; rather than something like &lt;code&gt;kmeans_colors_ex&lt;/code&gt; so that it&amp;rsquo;s more general and not bind to the internal algorithm.&lt;/p&gt;
&lt;p&gt;After the project created. We need to add &lt;code&gt;{:rustler, &amp;quot;~&amp;gt; 0.30.0&amp;quot;}&lt;/code&gt; to the dependency.&lt;/p&gt;
&lt;h2 id=&#34;create-rust-crate-inside&#34;&gt;Create Rust crate inside&lt;/h2&gt;
&lt;p&gt;Then we can create a native Rust lib inside our elixir package:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mix rustler.new
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then when it prompted asking for the Elixir module the native module registered to. Type &lt;code&gt;DominantColors&lt;/code&gt;, which is the module name &lt;code&gt;mix&lt;/code&gt; generated.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;This is the name of the Elixir module the NIF module will be registered to.
Module name &amp;gt; DominantColors
This is the name used &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; the generated Rust crate. The default is most likely fine.
Library name &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;dominantcolors&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &amp;gt;
* creating native/dominantcolors/.cargo/config.toml
* creating native/dominantcolors/README.md
* creating native/dominantcolors/Cargo.toml
* creating native/dominantcolors/src/lib.rs
* creating native/dominantcolors/.gitignore
Ready to go! See /Users/mikeli/projects/dominant_colors/native/dominantcolors/README.md &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; further instructions.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s loading the NIF. This is what it becomes&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;kd&#34;&gt;defmodule&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DominantColors&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;kn&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Rustler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;otp_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;crate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:dominantcolors&lt;/span&gt;

  &lt;span class=&#34;c1&#34;&gt;# When loading a NIF module, dummy clauses for all NIF function are required.&lt;/span&gt;
  &lt;span class=&#34;c1&#34;&gt;# NIF dummies usually just error out when called when the NIF is not loaded, as that should never normally happen.&lt;/span&gt;
  &lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_arg1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_arg2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:erlang&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nif_error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:nif_not_loaded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s try to add a simple test case to make sure the calling to Rust module works:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;n&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;add&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;n&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DominantColors&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;OK now we have the basic Elixir calling Rust working through &lt;code&gt;rustler&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;❯ mix &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt;
.
Finished in 0.00 seconds &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;0.00s async, 0.00s sync&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;m&#34;&gt;1&lt;/span&gt; test, &lt;span class=&#34;m&#34;&gt;0&lt;/span&gt; failures

Randomized with seed &lt;span class=&#34;m&#34;&gt;733558&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;integrate-kmeans-colors&#34;&gt;Integrate &lt;code&gt;kmeans-colors&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Now we can start the real work to integrate the Rust library. &lt;a href=&#34;https://github.com/okaneco/kmeans-colors&#34;&gt;https://github.com/okaneco/kmeans-colors&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So head over to &lt;code&gt;native/dominantcolors&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Running &lt;code&gt;cargo add kmeans-colors&lt;/code&gt; to add the dependency.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;cargo add kmeans-colors
cargo add palette
cargo add image
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Please notice we also added &lt;code&gt;palette&lt;/code&gt; and &lt;code&gt;image&lt;/code&gt; crate as well because we are going to use it to manipulate images and colors.&lt;/p&gt;
&lt;p&gt;Here we have the first version of it. It&amp;rsquo;s heavily inspired by the &lt;code&gt;kmeans-colors&lt;/code&gt;&amp;rsquo;s &lt;a href=&#34;https://github.com/okaneco/kmeans-colors/blob/master/src/bin/kmeans_colors/app.rs&#34;&gt;&lt;code&gt;cli&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;kmeans_colors&lt;/span&gt;::&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_kmeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Kmeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;use&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;palette&lt;/span&gt;::&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;FromColor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;IntoColor&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Lab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Srgb&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// reference
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// https://github.com/okaneco/kmeans-colors/blob/master/src/bin/kmeans_colors/app.rs
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;#[rustler::nif]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file_name&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;-&amp;gt; &lt;span class=&#34;nb&#34;&gt;Result&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Read file into buffer
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;img&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;image&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;img&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Can not open given file&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;());&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;img_vec&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;img&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;unwrap&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;to_rgb8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;().&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_raw&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Convert RGB [u8] buffer to Lab for k-means
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lab&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Lab&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;img_vec&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chunks&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;3&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Srgb&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;f32&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;255.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;f32&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;255.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;                &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;chunk&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;f32&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;/&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;255.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_format&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Perform k-means clustering
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;mut&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;best_result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Kmeans&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;converge&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mf&#34;&gt;5.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// 5 for LAB or 0.0025 for RGB; // threshold for convergence.
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runs&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// default just run once
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cluster&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;8&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// number of clusters/colors returned
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;max_iter&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;20&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// maximum number of iterations
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;verbose&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;in&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runs&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Run k-means multiple times
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run_result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_kmeans&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;cluster&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;max_iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;converge&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;verbose&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lab&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;i&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run_result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;score&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;best_result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;score&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;best_result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;run_result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// Convert Lab centroids back to Srgb&amp;lt;u8&amp;gt; for output
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color_codes&lt;/span&gt;: &lt;span class=&#34;nb&#34;&gt;Vec&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;best_result&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;centroids&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;iter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;Srgb&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;from_color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into_format&lt;/span&gt;::&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;u8&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;#{:02x}{:02x}{:02x}&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;red&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;green&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;blue&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;collect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nb&#34;&gt;Ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color_codes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;rustler&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;init&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Elixir.DominantColors&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I won&amp;rsquo;t go very deep on this, but I assume we will need more tweaks later on or expose more knobs to make it more customizable. But for now it should do the basic work.&lt;/p&gt;
&lt;p&gt;Then we can register this function in our Elixir module:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;kd&#34;&gt;defmodule&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DominantColors&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;kn&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Rustler&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;otp_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;crate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:dominantcolors&lt;/span&gt;

  &lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_arg1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:erlang&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nif_error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:nif_not_loaded&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Let&amp;rsquo;s write a test to validate it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;n&#34;&gt;test&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dominant_colors&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;./test/fixtures/test3.png&amp;#34;&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;assert&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DominantColors&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;bfbdbb&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;2f2e2e&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;a3a1a0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;d8653a&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;eeedec&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;171515&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;7e7b7b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;#&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;4d4b4b&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Works great 🎨.&lt;/p&gt;
&lt;h2 id=&#34;publish-to-hex&#34;&gt;Publish to hex&lt;/h2&gt;
&lt;p&gt;Now let&amp;rsquo;s publish it to hex and try to use it in Livebook.&lt;/p&gt;
&lt;p&gt;In order to publish the package, we would need to add couple of info to the &lt;code&gt;mix.exs&lt;/code&gt;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-diff&#34; data-lang=&#34;diff&#34;&gt;&lt;span class=&#34;gh&#34;&gt;diff --git a/mix.exs b/mix.exs
&lt;/span&gt;&lt;span class=&#34;gh&#34;&gt;index 7dd3f49..800a10c 100644
&lt;/span&gt;&lt;span class=&#34;gh&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;--- a/mix.exs
&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+++ b/mix.exs
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;@@ -7,7 +7,9 @@ defmodule DominantColors.MixProject do
&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;       version: &amp;#34;0.1.0&amp;#34;,
       elixir: &amp;#34;~&amp;gt; 1.15&amp;#34;,
       start_permanent: Mix.env() == :prod,
&lt;span class=&#34;gd&#34;&gt;-      deps: deps()
&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      deps: deps(),
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      description: description(),
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      package: package()
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;     ]
   end
 
&lt;span class=&#34;gu&#34;&gt;@@ -18,10 +20,23 @@ defmodule DominantColors.MixProject do
&lt;/span&gt;&lt;span class=&#34;gu&#34;&gt;&lt;/span&gt;     ]
   end
 
&lt;span class=&#34;gi&#34;&gt;+  defp description() do
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    &amp;#34;Extract dominant colors from given image (using kmeans), wrapper on top of rust kmeans_colors.&amp;#34;
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  end
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  defp package() do
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    [
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      name: &amp;#34;dominant_colors&amp;#34;,
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      licenses: [&amp;#34;MIT&amp;#34;],
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      links: %{&amp;#34;GitHub&amp;#34; =&amp;gt; &amp;#34;https://github.com/29decibel/dominant_colors&amp;#34;}
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+    ]
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+  end
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;   # Run &amp;#34;mix help deps&amp;#34; to learn about dependencies.
   defp deps do
     [
&lt;span class=&#34;gd&#34;&gt;-      {:rustler, &amp;#34;~&amp;gt; 0.30.0&amp;#34;}
&lt;/span&gt;&lt;span class=&#34;gd&#34;&gt;&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      {:rustler, &amp;#34;~&amp;gt; 0.30.0&amp;#34;},
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;+      {:ex_doc, &amp;#34;&amp;gt;= 0.0.0&amp;#34;, only: :dev, runtime: false}
&lt;/span&gt;&lt;span class=&#34;gi&#34;&gt;&lt;/span&gt;     ]
   end
 end
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Making sure you are registered or logged in into hex:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mix hex.user auth
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can publish the package. If you got the error of following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;** &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;UndefinedFunctionError&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;function&lt;/span&gt; Hex.Crypto.decrypt/3 is undefined &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;module Hex.Crypto is not available&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you can try the following:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mix local.hex --force
mix hex.publish
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If everything goes right, you should be able to see something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;Building dominant_colors 0.1.0
  Dependencies:
    rustler ~&amp;gt; 0.30.0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;app: rustler&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
  App: dominant_colors
  Name: dominant_colors
  Files:
    lib
    lib/dominant_colors.ex
    priv
    priv/native
    priv/native/libdominantcolors.so
    .formatter.exs
    mix.exs
    README.md
  Version: 0.1.0
  Build tools: mix
  Description: Extract dominant colors from given image &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;using kmeans&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, wrapper on top of rust kmeans_colors.
  Licenses: MIT
  Links:
    GitHub: https://github.com/29decibel/dominant_colors
  Elixir: ~&amp;gt; 1.15
Before publishing, please &lt;span class=&#34;nb&#34;&gt;read&lt;/span&gt; the Code of Conduct: https://hex.pm/policies/codeofconduct

Publishing package to public repository hexpm.

Proceed? &lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;Yn&lt;span class=&#34;o&#34;&gt;]&lt;/span&gt; y
Building docs...
Generating docs...
View &lt;span class=&#34;s2&#34;&gt;&amp;#34;html&amp;#34;&lt;/span&gt; docs at &lt;span class=&#34;s2&#34;&gt;&amp;#34;doc/index.html&amp;#34;&lt;/span&gt;
View &lt;span class=&#34;s2&#34;&gt;&amp;#34;epub&amp;#34;&lt;/span&gt; docs at &lt;span class=&#34;s2&#34;&gt;&amp;#34;doc/dominant_colors.epub&amp;#34;&lt;/span&gt;
Local password:
Publishing package...
&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#########################] 100%&lt;/span&gt;
Package published to https://hex.pm/packages/dominant_colors/0.1.0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;722129016b397b09a4a58349ab3f60ad12e4b24e09d1c5a301bd08a79ab18977&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
Publishing docs...
&lt;span class=&#34;o&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;#########################] 100%&lt;/span&gt;
Docs published to https://hexdocs.pm/dominant_colors/0.1.0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Our package is now available.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s create a dummy to consume it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mix new test_colors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then adding the dependency:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~&amp;gt; 0.1.0&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Opps, we got an error:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;spawn: Could not &lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; to native/dominantcolors

&lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; Compilation error in file lib/dominant_colors.ex &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt;
** &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;RuntimeError&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; calling &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;cargo metadata&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt; failed.

    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;rustler 0.30.0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; lib/rustler/compiler/config.ex:96: Rustler.Compiler.Config.metadata!/1
    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;rustler 0.30.0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; lib/rustler/compiler/config.ex:78: Rustler.Compiler.Config.build/1
    &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;rustler 0.30.0&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt; lib/rustler/compiler.ex:8: Rustler.Compiler.compile_crate/3
    lib/dominant_colors.ex:2: &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;module&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
could not compile dependency :dominant_colors, &lt;span class=&#34;s2&#34;&gt;&amp;#34;mix compile&amp;#34;&lt;/span&gt; failed. Errors may have been logged above. You can recompile this dependency with &lt;span class=&#34;s2&#34;&gt;&amp;#34;mix deps.compile dominant_colors --force&amp;#34;&lt;/span&gt;, update it with &lt;span class=&#34;s2&#34;&gt;&amp;#34;mix deps.update dominant_colors&amp;#34;&lt;/span&gt; or clean it with &lt;span class=&#34;s2&#34;&gt;&amp;#34;mix deps.clean dominant_colors&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looks like we forgot the add the rust files into the package, that&amp;rsquo;s why it can&amp;rsquo;t find certain files in the published package. To fix this, make sure to include the rust files:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;defp&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
      &lt;span class=&#34;ss&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;lib&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors/.cargo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors/src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors/Cargo*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;mix.exs&amp;#34;&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
      &lt;span class=&#34;n&#34;&gt;...&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this, we should be able to consume the package.&lt;/p&gt;
&lt;h2 id=&#34;rustler-pre-compilation&#34;&gt;Rustler Pre-compilation&lt;/h2&gt;
&lt;p&gt;This works great if the consumer end have the Rust toolchain ready. However, in the context of Livebook this won&amp;rsquo;t work by default because Livebook do not have the access to the Rust toolchain like &lt;code&gt;cargo&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Luckily &lt;a href=&#34;https://hexdocs.pm/rustler_precompiled/precompilation_guide.html&#34;&gt;rustler_precompiled&lt;/a&gt; is exactly trying to solve this.&lt;/p&gt;
&lt;p&gt;The TLDR version of &lt;code&gt;rustler_precompied&lt;/code&gt; is that it uses a CI server to pre-compile a bunch of binaries, then tell the Elixir to download those binary directly during installation. Think of it as a &lt;strong&gt;multi stage&lt;/strong&gt; process.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Using CI server to pre compile binary. This is necessary to support multiple architecture machine (X86, ARM, Linux, PC, Mac &amp;hellip;)&lt;/li&gt;
&lt;li&gt;Save precompiled binary along with version number in Github artifacts.&lt;/li&gt;
&lt;li&gt;Pointing the Elixir module to download that (with checksum).&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;create-github-action-workflow&#34;&gt;Create Github action workflow&lt;/h3&gt;
&lt;p&gt;First let&amp;rsquo;s create a GitHub action workflow. Heavily borrowed from &lt;a href=&#34;https://github.com/philss/rustler_precompilation_example/tree/main&#34;&gt;rustler_precompilation_example&lt;/a&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Build precompiled NIFs&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;push&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;branches&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;main&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;tags&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;*&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;jobs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;build_release&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;NIF ${{ matrix.nif }} - ${{ matrix.job.target }} (${{ matrix.job.os }})&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;runs-on&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ matrix.job.os }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;strategy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;fail-fast&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;false&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;matrix&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nif&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2.16&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;2.15&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;job&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;arm-unknown-linux-gnueabihf,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-20.04,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use-cross&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;}&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;aarch64-unknown-linux-gnu,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-20.04,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use-cross&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;}&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;# commentted this out as I am experiencing some compilation issues&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;#   - {&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;#       target: aarch64-unknown-linux-musl,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;#       os: ubuntu-20.04,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;#       use-cross: true,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;c&#34;&gt;#     }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target: aarch64-apple-darwin, os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;macos-11 }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;riscv64gc-unknown-linux-gnu,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-20.04,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use-cross&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;}&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target: x86_64-apple-darwin, os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;macos-11 }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target: x86_64-unknown-linux-gnu, os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-20.04 }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;x86_64-unknown-linux-musl,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;ubuntu-20.04,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;              &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use-cross&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;}&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target: x86_64-pc-windows-gnu, os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;windows-2019 }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;- {&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target: x86_64-pc-windows-msvc, os&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;windows-2019 }&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;steps&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Checkout source code&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/checkout@v3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Extract project version&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;shell&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;bash&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;run&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;          # Get the project version from mix.exs
&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;          echo &amp;#34;PROJECT_VERSION=$(sed -n &amp;#39;s/^  @version &amp;#34;\(.*\)&amp;#34;/\1/p&amp;#39; mix.exs | head -n1)&amp;#34; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Install Rust toolchain&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dtolnay/rust-toolchain@stable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;toolchain&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;stable&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ matrix.job.target }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Build the project&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;build-crate&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;philss/rustler-precompiled-action@v1.0.1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;project-name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;dominantcolors&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;project-version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ env.PROJECT_VERSION }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ matrix.job.target }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;nif-version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ matrix.nif }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;use-cross&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ matrix.job.use-cross }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;project-dir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Artifact upload&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;actions/upload-artifact@v3&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ steps.build-crate.outputs.file-name }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ steps.build-crate.outputs.file-path }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;nt&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;Publish archives and packages&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;uses&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;softprops/action-gh-release@v1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;          &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;|&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;sd&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;w&#34;&gt;            &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;${{ steps.build-crate.outputs.file-path }}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;startsWith(github.ref, &amp;#39;refs/tags/&amp;#39;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then add the &lt;code&gt;rustler_precompiled&lt;/code&gt; dependency:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:rustler_precompiled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~&amp;gt; 0.7&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also add &lt;code&gt;Cross.toml&lt;/code&gt; in the &lt;code&gt;native/dominantcolors&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;passthrough&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;RUSTLER_NIF_VERSION&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the &lt;code&gt;.cargo/config.toml&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-toml&#34; data-lang=&#34;toml&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;cfg(target_os = &amp;#34;macos&amp;#34;)&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;rustflags&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-C&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;link-arg=-undefined&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;-C&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;link-arg=dynamic_lookup&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;


&lt;span class=&#34;c&#34;&gt;# See https://github.com/rust-lang/rust/issues/59302&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;target&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;x86_64-unknown-linux-musl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;rustflags&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-C&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;target-feature=-crt-static&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

&lt;span class=&#34;c&#34;&gt;# Provides a small build size, but takes more time to build.&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;profile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;lto&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we can push the changes to see if the build is green.&lt;/p&gt;
&lt;p&gt;Finally after we validated its build success. We can release it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;git tag 0.1.0
git push --tags
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we have the binaries built in the CI, we can grab them and use them instead.&lt;/p&gt;
&lt;h3 id=&#34;consume-pre-compiled-binaries&#34;&gt;Consume pre-compiled binaries&lt;/h3&gt;
&lt;p&gt;In order to get that, we need to do two things:&lt;/p&gt;
&lt;p&gt;First we need to use the precompile macro:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;  &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Mix.Project&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;config&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;

  &lt;span class=&#34;kn&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;RustlerPrecompiled&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;otp_app&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;crate&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;dominantcolors&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;base_url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;https://github.com/29decibel/dominant_colors/releases/download/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;force_build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;System&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;get_env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;DOMINANT_COLORS_BUILD&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;in&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then we need to download the checksum:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;mix rustler_precompiled.download DominantColors --all
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we should be able to use the precompiled version locally.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note we also provide a &lt;code&gt;DOMINANT_COLORS_BUILD&lt;/code&gt; as a way to escape the pre compiled binary. It&amp;rsquo;s extremely useful for local development.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;publish-to-hex-1&#34;&gt;Publish to hex&lt;/h3&gt;
&lt;p&gt;Last step, let&amp;rsquo;s publish it to hex. Before the publish, we will need to make sure the &lt;code&gt;checksum*&lt;/code&gt; files are included in the package, otherwise the installation will be failed with &lt;code&gt;checksum not matched error&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;  &lt;span class=&#34;kd&#34;&gt;defp&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;package&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
      &lt;span class=&#34;ss&#34;&gt;files&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;lib&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors/.cargo&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors/src&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;native/dominantcolors/Cargo*&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;checksum-*.exs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;mix.exs&amp;#34;&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
      &lt;span class=&#34;n&#34;&gt;...&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can verify the final package contents by using &lt;code&gt;mix hex.build --unpack&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;❯ mix hex.build --unpack
Building dominant_colors 0.1.1
  Dependencies:
    rustler ~&amp;gt; 0.30.0 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;app: rustler&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
    rustler_precompiled ~&amp;gt; 0.7 &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;app: rustler_precompiled&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;
  App: dominant_colors
  Name: dominant_colors
  Files:
    lib
    lib/dominant_colors.ex
    native/dominantcolors/.cargo
    native/dominantcolors/.cargo/config.toml
    native/dominantcolors/src
    native/dominantcolors/src/lib.rs
    native/dominantcolors/Cargo.lock
    native/dominantcolors/Cargo.toml
    checksum-Elixir.DominantColors.exs
    mix.exs
  Version: 0.1.1
  Build tools: mix
  Description: Extract dominant colors from given image &lt;span class=&#34;o&#34;&gt;(&lt;/span&gt;using kmeans&lt;span class=&#34;o&#34;&gt;)&lt;/span&gt;, wrapper on top of rust kmeans_colors.
  Licenses: MIT
  Links:
    GitHub: https://github.com/29decibel/dominant_colors
  Elixir: ~&amp;gt; 1.15
Saved to dominant_colors-0.1.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Looks great! Now we can publish it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mix hex.publish
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;using-it-in-livebook&#34;&gt;Using it in Livebook&lt;/h2&gt;
&lt;p&gt;Add dependency:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;~&amp;gt; 0.1.3&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then some Elixir code and using Kino render:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;n&#34;&gt;image_path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/Users/mikeli/Downloads/111ebfdc244345c3.png&amp;#34;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:ok&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;DominantColors&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;dominant_colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;image_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;styles&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;
   &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;justify-content&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;center&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;align-items&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;center&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;white&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;display&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;inline-flex&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;width&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;100px&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;height&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;30px&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;background-color&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;},&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;margin&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;1px&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;k&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;k&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;: &amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;v&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;;&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34; &amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;n&#34;&gt;html&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; 
  &lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;colors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;fn&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;color&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;div style=\&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;styles&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;\&amp;#34;&amp;gt;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;color&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;lt;/div&amp;gt;&amp;#34;&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;

&lt;span class=&#34;nc&#34;&gt;Kino.HTML&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;How lovely!&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I love the fact that Elixir can leverage so much in the Rust land with reasonable effort. It opened a whole new world to Elixir and Livebook users.&lt;/p&gt;
&lt;p&gt;Next time, when you can&amp;rsquo;t find a library in the Elixir land, give some Rust crate and &lt;code&gt;rustler&lt;/code&gt; a try.&lt;/p&gt;
&lt;h2 id=&#34;credits&#34;&gt;Credits&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/rusterlium/rustler&#34;&gt;rustler&lt;/a&gt; The one makes all this possible&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/philss/rustler_precompiled&#34;&gt;rustler_precompiled&lt;/a&gt; Makes consuming rust based Elixir lib seamless&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://docs.rs/kmeans_colors/latest/kmeans_colors/&#34;&gt;kmeans-colors&lt;/a&gt; Rust library to get dominant colors.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://github.com/avencera/fast_rss/tree/master&#34;&gt;fast_rss&lt;/a&gt; Real world example of RSS parser using rustler.&lt;/li&gt;
&lt;li&gt;&lt;a href=&#34;https://teenage.engineering/products/ep-133&#34;&gt;Teenage Engineering EP 133&lt;/a&gt; Just a beautiful object. And colors too.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;There are lots of other things we can improve for sure. But not a bad start I would say.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;optimize the performance for big images&lt;/li&gt;
&lt;li&gt;expose more knobs to tune the result&lt;/li&gt;
&lt;li&gt;return percentage of each color (assign weight to the color returned)&lt;/li&gt;
&lt;/ul&gt;
</description>
    </item>
    
    
    
    <item>
      <title>📨 Void Inbox</title>
      <link>https://orchardlab.dev/posts/void-inbox/</link>
      <pubDate>Sat, 18 Nov 2023 08:45:14 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/void-inbox/</guid>
      <description>&lt;p&gt;Over the past weekend, I created a web app called &lt;strong&gt;VoidInbox&lt;/strong&gt; using Phoenix and Elixir. Which can let me receive newsletters or other not so important emails, and read them there. It looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/void-inbox/list.png&#34; alt=&#34;void inbox home screen&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;why-am-i-building-this&#34;&gt;Why am I building this&lt;/h2&gt;
&lt;p&gt;With the flourishing of the thousands of newsletters. I&amp;rsquo;ve always found myself lost in the pile of emails in my inbox. Not only it messes with my &amp;ldquo;actual&amp;rdquo; emails, but also it&amp;rsquo;s hard to read them as well.&lt;/p&gt;
&lt;p&gt;Plus I am not comfortable to share my personal email address to random newsletters or email lists.&lt;/p&gt;
&lt;p&gt;So I am thinking to build one, a simple enough where I can still confidently subscribe to the newsletters I found useful, where I can read them in one place, but also give me the peace of mind of not letting anyone bombarding my email inboxes.&lt;/p&gt;
&lt;p&gt;On top of that, I want to share the process of the creation. I truly believe in open source and self hosting software. But I think we can go even further by sharing the creation process. Hopefully even if people don&amp;rsquo;t find the software itself is useful, they still can benefit from some of the thinking or ideas within it.&lt;/p&gt;
&lt;p&gt;I like building things, I think that&amp;rsquo;s the ultimately form of finding long lasting happiness. Putting them into the writing form not only helps me to have more clear thoughts, but also enforce me to have some level of rigorous. Bottom line is I might found the thing I wrote useful for my future self.&lt;/p&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;Over the next couple of weeks, I am gonna share the pieces by piece how I build this. It would include but not limited to core, models, UI, deployment etc. Hopefully you can get something useful from it or at least get inspired to create something your own as well.&lt;/p&gt;
&lt;p&gt;I love the article &lt;a href=&#34;https://world.hey.com/jason/teaching-iteration-2787a665&#34;&gt;Teaching Iteration&lt;/a&gt; from Jason Fried. Where I think it&amp;rsquo;s way more relevant from the software projects perspective, as the software we produced most likely would evolve overtime. By learning how to take something from naive and primitive to useful and beautiful takes time and practice, so I am hoping by doing this to &lt;strong&gt;practice the iteration process&lt;/strong&gt; as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Here is the general agenda of what I am gonna to share in the next couple of weeks:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Core.&lt;/strong&gt; SMTP server to receive email and parse them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Models.&lt;/strong&gt; Database models and context to power the app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;UI&lt;/strong&gt;. Build a simple and minimal UI using LiveView.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deployment.&lt;/strong&gt; Deploy it to the cloud provider (fly.io) or using docker compose in any machines.&lt;/li&gt;
&lt;li&gt;More features &amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Stay tuned to subscribe the &lt;a href=&#34;https://orchardlab.dev/feed.xml&#34;&gt;RSS feed&lt;/a&gt;. At the same time, give it a shot to the demo version of it on &lt;a href=&#34;https://void-inbox.fly.dev/&#34;&gt;https://void-inbox.fly.dev/&lt;/a&gt; and let me know what you think.&lt;/p&gt;
&lt;p&gt;Or if you can&amp;rsquo;t wait to get your hands dirty on the source code, you can head over to &lt;a href=&#34;https://github.com/29decibel/void_inbox&#34;&gt;29decibel/void_inbox&lt;/a&gt; giving it a spin as well.&lt;/p&gt;
&lt;p&gt;See you in the next post.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Fall in love with the questions</title>
      <link>https://orchardlab.dev/posts/questions/</link>
      <pubDate>Fri, 06 Oct 2023 06:16:32 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/questions/</guid>
      <description>&lt;p&gt;I came across a great book called &lt;a href=&#34;https://www.goodreads.com/book/show/57185177-the-socratic-method&#34;&gt;The Socratic Method&lt;/a&gt; by Ward Farnsworth, which was recommended by Rich Hickey in his talk &lt;a href=&#34;https://www.youtube.com/watch?v=c5QF2HjHLSE&#34;&gt;Design in Practice&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s an amazing book, easily the best one I&amp;rsquo;ve read this year. The ideas in the book have helped me to connect many thoughts I had before on &amp;ldquo;questions&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Socratic_method&#34;&gt;The Socratic method&lt;/a&gt; is a style of thought and a method of inquiry that involves asking questions to stimulate critical thinking and to uncover underlying assumptions. It&amp;rsquo;s not about definitively answering hard questions, but rather about asking and pursuing them in order to arrive at wisdom.&lt;/p&gt;
&lt;p&gt;One change I made every morning this year is to change &lt;em&gt;TODO&lt;/em&gt; items to questions. Instead of writing &amp;ldquo;Fix the issue of slow builds&amp;rdquo;, I write &amp;ldquo;Why the our build takes ages to finish?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;On the surface, it might look very subtle or maybe even jokingly funny (obviously we can&amp;rsquo;t change every &lt;em&gt;TODO&lt;/em&gt; into questions as well like booking a dental appointment for my kids). But I found myself way more engaged to solve the problem than merely following the directions.&lt;/p&gt;
&lt;p&gt;More importantly, it has me naturally asking more and more questions. So you could easily see the trajectory of the problem solving process. By the end of the day, you just somehow feel you&amp;rsquo;ve learned more than merely solving that one problem. Those questions linger in your mind way longer than the &lt;em&gt;TODO&lt;/em&gt; list.&lt;/p&gt;
&lt;p&gt;One known common technique we use in postmortems is &lt;a href=&#34;https://en.wikipedia.org/wiki/Five_whys&#34;&gt;&lt;strong&gt;Five Whys&lt;/strong&gt;&lt;/a&gt;. Sometimes it feels intense (interrogative), but most of the time, everyone ended up with a more clear picture of the overall situation and concrete ways to prevent it in the future.&lt;/p&gt;
&lt;p&gt;At the same time, asking questions couldn&amp;rsquo;t be more relevant in the age of LLMs/ChatGPT. I once shared with my colleagues that I think the era of &amp;ldquo;fearing to ask stupid questions is gone&amp;rdquo;. Thanks to ChatGPT, anyone can ask any questions anytime without worrying the embarrassment.&lt;/p&gt;
&lt;p&gt;I certainly found myself asking many more questions this year thanks to ChatGPT. Partly because lots of questions I have are so customized or context specific which was very hard to search or even find the right audience to ask. So I just gave up asking it all together. But now I can just briefly layout the situation and the challenge, let the LLM handle the rest of clarification or refinement of the questions.&lt;/p&gt;
&lt;p&gt;Yes we might ask many &lt;em&gt;bad&lt;/em&gt; questions, but aren&amp;rsquo;t they better than no questions?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every time you ask and answer good questions, your understanding gets a bit deeper. You better understand the other side and the weaknesses on your side. You see more complexity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;A question puts pressure on whoever receives it. If you ask questions of yourself, you are the recipient of the pressure. That&amp;rsquo;s good. Stating an opinion is roughly the opposite. It releases pressure. Pressure is uncomfortable, so most people think and talk in opinions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The Socratic Method&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;One quote I really like is &amp;ldquo;Fall in love with problem, not the solution.&amp;rdquo; (Oh, there&amp;rsquo;s even a &lt;a href=&#34;https://www.goodreads.com/book/show/61131333-fall-in-love-with-the-problem-not-the-solution&#34;&gt;book&lt;/a&gt; now)&lt;/p&gt;
&lt;p&gt;So let&amp;rsquo;s &amp;ldquo;Fall in love with the questions, not the answers.&amp;rdquo;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Bun &#43; Prisma</title>
      <link>https://orchardlab.dev/posts/bun-prisma/</link>
      <pubDate>Sun, 17 Sep 2023 09:00:30 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/bun-prisma/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;I just can&amp;rsquo;t stop having fun using &lt;a href=&#34;https://bun.sh&#34;&gt;bun&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have an old postgresql database backup. I need to quickly inspect the tables/models in the database and then extract data from it.&lt;/p&gt;
&lt;h2 id=&#34;spin-up-postgres&#34;&gt;Spin up postgres&lt;/h2&gt;
&lt;p&gt;After having the backup sql ready &lt;code&gt;output-backup.sql&lt;/code&gt;. I created a &lt;code&gt;docker-compose.yml&lt;/code&gt; file to quickly spin up a postgres instance.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-yml&#34; data-lang=&#34;yml&#34;&gt;&lt;span class=&#34;nt&#34;&gt;version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;3.1&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;services&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;image&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres:latest&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;environment&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_USER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;postgres&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;POSTGRES_DB&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;l&#34;&gt;example_dev&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;ports&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;s2&#34;&gt;&amp;#34;54321:5432&amp;#34;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;      &lt;/span&gt;- &lt;span class=&#34;l&#34;&gt;postgres_data:/var/lib/postgresql/data&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;volumes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;postgres_data&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s re-dump the data back into the local database.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;psql -U postgres -h localhost -p &lt;span class=&#34;m&#34;&gt;54321&lt;/span&gt; -d example_dev -f output-backup.sql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Okay the fun parts begin.&lt;/p&gt;
&lt;h2 id=&#34;bun--prisma-fastlane&#34;&gt;Bun + Prisma Fastlane&lt;/h2&gt;
&lt;p&gt;Set up a new package using &lt;code&gt;bun&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;mkdir data-explorer
&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; data-explorer &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; bun init -y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now let&amp;rsquo;s add prisma:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bun add prisma
bunx prisma init --datasource-provider postgresql
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Remember to change the DATABASE_URL to our local one in &lt;code&gt;.env&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;DATABASE_URL=&amp;#34;postgresql://postgres:postgres@localhost:54321/example_dev?schema=public&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Inspect the database now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bunx prisma db pull
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Generate prisma client:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bunx prisma generate
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hooray!&lt;/p&gt;
&lt;p&gt;Now we can play with the database:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-ts&#34; data-lang=&#34;ts&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PrismaClient&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;@prisma/client&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;prisma&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;new&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PrismaClient&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allUsers&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;prisma&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;users&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;findMany&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;allUsers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;length&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;See it now:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bun run index.ts
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The beauty of Prisma is that the generated &lt;code&gt;schema.prisma&lt;/code&gt; is like a X-Ray to the database. Not only we can see the tables and relations, but also we can manupulate it in a type safe way (with autocompletion of course).&lt;/p&gt;
&lt;p&gt;Such a joyful experience.&lt;/p&gt;
&lt;h2 id=&#34;summary&#34;&gt;Summary&lt;/h2&gt;
&lt;p&gt;I still can&amp;rsquo;t believe how easy it is to run a TypeScript project using &lt;code&gt;bun&lt;/code&gt; with massive npm modules without losing my mind.&lt;/p&gt;
&lt;h2 id=&#34;references&#34;&gt;References&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://bun.sh/guides/ecosystem/prisma&#34;&gt;https://bun.sh/guides/ecosystem/prisma&lt;/a&gt;
&lt;a href=&#34;https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/introspection-typescript-postgresql&#34;&gt;https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/introspection-typescript-postgresql&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Playground</title>
      <link>https://orchardlab.dev/posts/playground/</link>
      <pubDate>Sat, 09 Sep 2023 09:32:46 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/playground/</guid>
      <description>&lt;p&gt;I love &lt;code&gt;tmp&lt;/code&gt; folder driven development, it gives me the freedom to experiment without worrying about polluting things.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;tmp&lt;/code&gt; folder driven development:&lt;/p&gt;
&lt;p&gt;Start with tmp folder and messing around libraries or scripts before put it into the actual project.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Recently I&amp;rsquo;ve had a great time using &lt;a href=&#34;https://bun.sh&#34;&gt;bun&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Develop, test, run, and bundle JavaScript &amp;amp; TypeScript projects—all with Bun. Bun is an all-in-one JavaScript runtime &amp;amp; toolkit designed for speed, complete with a bundler, test runner, and Node.js-compatible package manager.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All that means to me is that I can write straight-up TypeScript and using any npm packages without messing around with endless configurations and build tools.&lt;/p&gt;
&lt;h2 id=&#34;idea&#34;&gt;Idea&lt;/h2&gt;
&lt;p&gt;While I am building a desktop app, one feature I want to have is to get the cover image of the pdf file.&lt;/p&gt;
&lt;p&gt;So, instead of immediately adding npm packages directly to my project. I want to quickly try it out first.&lt;/p&gt;
&lt;h2 id=&#34;playground&#34;&gt;Playground&lt;/h2&gt;
&lt;p&gt;Playground is such an interesting and powerful concept if you think about it. It&amp;rsquo;s safe, sandbox-ed.&lt;/p&gt;
&lt;p&gt;To set up a playground for my experiment. I did the following (thanks to &lt;code&gt;bun&lt;/code&gt;):&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; /tmp
mkdir pdf-cover
bun init -y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That&amp;rsquo;s it. Now I can just start writing TypeScript and start adding libraries.&lt;/p&gt;
&lt;h2 id=&#34;assumption&#34;&gt;Assumption&lt;/h2&gt;
&lt;p&gt;The assumption is to read the first page of the pdf and convert it into an image. As that&amp;rsquo;s where most of the pdf (especially books) place their cover image.&lt;/p&gt;
&lt;h2 id=&#34;experiment&#34;&gt;Experiment&lt;/h2&gt;
&lt;p&gt;To extract the first page of the pdf, we can use &lt;code&gt;pdf-lib&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bun add pdf-lib
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here is the code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfFilePath&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;example.pdf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;c1&#34;&gt;// Read the existing PDF into a buffer
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;existingPdfBytes&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Buffer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;readFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfFilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// Load the PDF with pdf-lib
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PDFDocument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;existingPdfBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// Create a new PDF document
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PDFDocument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// Copy the cover page from the existing PDF into the new PDF
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;copyPages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

&lt;span class=&#34;c1&#34;&gt;// Serialize the PDF to bytes and write it to the file system
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfBytes&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Uint8Array&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverPdfPath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cover_page.pdf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;writeFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverPdfPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Running our script is dead simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bun run index.ts
&lt;span class=&#34;c1&#34;&gt;# Because it calls graphicsmagick underneath&lt;/span&gt;
brew install graphicsmagick
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now we get the cover page pdf. Next let&amp;rsquo;s try to convert it into an image.&lt;/p&gt;
&lt;p&gt;To do that. We add &lt;code&gt;pdf2pic&lt;/code&gt; package.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;bun add pdf2pic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;pdf2pic&lt;/code&gt; can convert existing pdf to an image given some options.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverOutputPathParts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parseFilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverOutputPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
 &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;options&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;density&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;saveFilename&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;cover-image&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;savePath&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;./&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;.png&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;width&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;600&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
   &lt;span class=&#34;nx&#34;&gt;height&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;600&lt;/span&gt;
 &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
 &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;convert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fromPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;cover_page.pdf&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
 &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pageToConvertAsImage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

 &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;convert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pageToConvertAsImage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;responseType&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then let&amp;rsquo;s run our script again using &lt;code&gt;bun run index.ts&lt;/code&gt;. Cool, now we get the image we want.&lt;/p&gt;
&lt;p&gt;However, the image size is not well-proportioned as the original pdf dimensions. So we need to figure out the right dimension of the original pdf.&lt;/p&gt;
&lt;p&gt;We can then go ahead write a function to figure that out:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getCoverPageDimensions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfFile&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;width&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;height&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;c1&#34;&gt;// Read the existing PDF into a buffer
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;existingPdfBytes&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Buffer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;readFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfFile&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

  &lt;span class=&#34;c1&#34;&gt;// Load the PDF with pdf-lib
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PDFDocument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;existingPdfBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

  &lt;span class=&#34;c1&#34;&gt;// Get the cover page
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverPage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getPages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;

  &lt;span class=&#34;c1&#34;&gt;// Get the dimensions
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;modularize&#34;&gt;Modularize&lt;/h2&gt;
&lt;p&gt;Now we have the proof of concept, without any of those fighting with modules, TypeScript setup, build configurations, ts-node.&lt;/p&gt;
&lt;p&gt;In order to consume this, let&amp;rsquo;s turn it into a module (a file with exported functions) instead. So that it can act as a well isolated unit.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s define the interface we want:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;extractAndConvertCoverPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;pdfFilePath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;coverOutputPath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Also as you notice from the previous code, it&amp;rsquo;s quite messy and inefficiencies. Which is totally fine, because that&amp;rsquo;s the point of playground. We want to get to a working version first and validate our ideas, then into the optimization phase.&lt;/p&gt;
&lt;p&gt;The beauty of the interface is that we can hide all the inefficiencies behind the scene as well. So we can improve it without impacting the callers.&lt;/p&gt;
&lt;p&gt;After some optimization here is the final code:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PDFDocument&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pdf-lib&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fromBuffer&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;pdf2pic&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;path&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;OUTPUT_IMAGE_DPI&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;200&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;export&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;extractPDFCoverImage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;pdfFilePath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;coverOutputPath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;try&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// Read the existing PDF into a buffer
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;existingPdfBytes&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Buffer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;readFileSync&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfFilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Load the PDF with pdf-lib
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PDFDocument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;existingPdfBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfDimensions&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getCoverPageDimensions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// we need a new pdf doc to get buffer which will be used by pdf2pic
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;PDFDocument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;create&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Copy the cover page from the existing PDF into the new PDF
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;copyPages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]);&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;addPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverPage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// Serialize the PDF to bytes and write it to the file system
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfBytes&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;Uint8Array&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;save&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;

    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;newPdfBuffer&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Buffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;from&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newPdfBytes&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;

    &lt;span class=&#34;c1&#34;&gt;// now we can start converting the pdf to image
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverOutputPathParts&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parseFilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverOutputPath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;options&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;density&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;OUTPUT_IMAGE_DPI&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;coverOutputPathParts&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;...&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfDimensions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;convert&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;fromBuffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;newPdfBuffer&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;options&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
    &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pageToConvertAsImage&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

    &lt;span class=&#34;k&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;convert&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pageToConvertAsImage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;responseType&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;image&amp;#34;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;});&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;Error:&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;parseFilePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;absolutePath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;ext&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;extname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;absolutePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;substring&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Get extension without the dot
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;filename&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;basename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;absolutePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`.&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Get filename without extension
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;directory&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dirname&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;absolutePath&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// Get directory path
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;saveFilename&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;filename&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;savePath&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;directory&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;format&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;ext&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;getCoverPageDimensions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;PDFDocument&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;Promise&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;width&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;height&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;number&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverPagePdf&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;pdfDoc&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getPages&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()[&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;];&lt;/span&gt;
  &lt;span class=&#34;c1&#34;&gt;// Get the dimensions
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;coverPagePdf&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;getSize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;a href=&#34;https://gist.github.com/29decibel/3406ed5139f8b073d8ecea4fad4799d5&#34;&gt;Gist link&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;learnings&#34;&gt;Learnings&lt;/h2&gt;
&lt;p&gt;All-in-one tools are great, fast tools are great. Not because they are more efficient, which of course they did. But more importantly, they don&amp;rsquo;t get in the way between you and your ideas.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=PUv66718DII&#34;&gt;Ideas are fragile&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Don&amp;rsquo;t get lost before you even start experimenting and cultivating your ideas.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s do more &lt;code&gt;tmp&lt;/code&gt; folder driven development, let&amp;rsquo;s do more playgrounds, let&amp;rsquo;s do more &lt;code&gt;bun&lt;/code&gt; 🚀!&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>How do you feel</title>
      <link>https://orchardlab.dev/posts/feel/</link>
      <pubDate>Sat, 02 Sep 2023 09:49:18 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/feel/</guid>
      <description>&lt;p&gt;With every single sophisticated tool nowadays, we are constantly measured by numbers: how well we perform, how well we accomplish certain tasks, how effective we were in our training.&lt;/p&gt;
&lt;p&gt;However, we still rarely measure how we actually feel.&lt;/p&gt;
&lt;p&gt;The first time I encountered this question was actually from my Suunto watch. By the end of the running workout, on top of showing the running summary, it will ask, &amp;ldquo;How was it? (Excellent, Good, OK, Poor ..)&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Suddenly, I realized that what we feel matters. Because there are just so many things stats and completion rates cannot capture. And how we feel matters in the longevity of the training. Sometimes, even if the stats show perfect numbers, but if we constantly feel terrible, we might risk ourselves getting injured or burnt out.&lt;/p&gt;
&lt;p&gt;Asking &amp;ldquo;How do you feel?&amp;rdquo; can create a sense of being cared for, which may enhance the psychological benefits of exercise and contribute to a more positive overall experience.&lt;/p&gt;
&lt;p&gt;At &lt;a href=&#34;https://www.betterup.com&#34;&gt;BetterUp&lt;/a&gt;, I love the conscious checking part of our sprint retro meeting, which I never had before. In the past, all we cared about was how many tickets we closed, how well we could estimate the next sprint, but no one cared about how you feel.&lt;/p&gt;
&lt;p&gt;To be clear, I don&amp;rsquo;t think asking &amp;ldquo;how do you feel&amp;rdquo; is the ultimate cure for team productivity, but I can say, I feel much more motivated and recharged after just casually chatting about our feelings, or putting those in my journal.&lt;/p&gt;
&lt;p&gt;The same can apply to the products we use. Sometimes we can&amp;rsquo;t articulate why we prefer certain tools over others, but they just make us feel good (&lt;a href=&#34;https://www.youtube.com/watch?v=5kFc5-D4PUs&#34;&gt;Apple WWDC 2013 - What do we want people to feel?&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Next time, try to ask more of yourself (or others), &amp;ldquo;How do I/you feel?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;So the next time you find yourself overwhelmed by metrics and numbers, take a moment to step back and listen to your feelings. After all, a little emotional self-check can make the journey, whether it&amp;rsquo;s a sprint retro or a morning jog, all the more meaningful.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Tools</title>
      <link>https://orchardlab.dev/posts/tools/</link>
      <pubDate>Sat, 26 Aug 2023 09:46:48 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/tools/</guid>
      <description>&lt;p&gt;Great tools enable us to focus and unleash our creativity. They either allow us to do things we couldn&amp;rsquo;t do before or provide us with new perspectives we hadn&amp;rsquo;t considered.&lt;/p&gt;
&lt;p&gt;Here are a couple of tools that have stayed with me for a long time, and I am grateful to have them. I am a macOS user, so some of these tools are only available on this platform. However, you can find alternatives on other platforms as well.&lt;/p&gt;
&lt;h2 id=&#34;alfred&#34;&gt;Alfred&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://www.alfredapp.com&#34;&gt;https://www.alfredapp.com&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Alfred is an award-winning app for macOS which boosts your efficiency with hotkeys, keywords, text expansion and more. Search your Mac and the web, and be more productive with custom actions to control your Mac.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;To me, Alfred is an integral part of macOS; it&amp;rsquo;s the first thing I install on any macOS system.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a launcher, but with an extremely customizable workflow. I love its simplicity; everything is displayed in a list format. Each item on the list contains just three elements: title, description, and icon.&lt;/p&gt;
&lt;p&gt;While at Amazon, I even created an Alfred workflow for internal use, allowing developers to search for packages, pipelines, wikis, and contacts. Thousands of developers at Amazon used it.&lt;/p&gt;
&lt;p&gt;You can easily create &lt;a href=&#34;https://www.alfredapp.com/workflows/&#34;&gt;workflows&lt;/a&gt; integrating with your company&amp;rsquo;s unique tools or workflows as well using any programming language.&lt;/p&gt;
&lt;p&gt;Raycast is the new kid in this space, offering more sophisticated and rich user interactions. I tried it a couple of times but eventually returned to Alfred for its simplicity.&lt;/p&gt;
&lt;h2 id=&#34;iterm2&#34;&gt;iTerm2&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://iterm2.com&#34;&gt;https://iterm2.com&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;iTerm2 is a replacement for Terminal and the successor to iTerm. It works on Macs with macOS 10.14 or newer. iTerm2 brings the terminal into the modern age with features you never knew you always wanted.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Solid terminal with very few surprises. It&amp;rsquo;s just so fast.&lt;/p&gt;
&lt;p&gt;Warp is the new kid in this space, but I found it to be &amp;ldquo;too helpful&amp;rdquo; at times.&lt;/p&gt;
&lt;h2 id=&#34;tmux&#34;&gt;Tmux&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://github.com/tmux/tmux&#34;&gt;https://github.com/tmux/tmux&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;tmux is a terminal multiplexer. It lets you switch easily between several programs in one terminal, detach them (they keep running in the background) and reattach them to a different terminal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We usually work in a &amp;ldquo;workspace&amp;rdquo; manner, meaning we run multiple tasks for one project—logs, frontend, backend, monitoring.&lt;/p&gt;
&lt;p&gt;Tmux is like the express lane that enables you to quickly jump back into (restore) the context you left. You can start a development session with multiple tasks running—using Vim, tailing logs, and configuring the frontend—in a single session.&lt;/p&gt;
&lt;p&gt;The beauty is the next day, you can instantly back to the entire context where you left the day before.&lt;/p&gt;
&lt;h2 id=&#34;ia-writer&#34;&gt;iA Writer&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://ia.net/writer&#34;&gt;https://ia.net/writer&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;iA Writer gives you the essence of pure writing by providing an unobtrusive interface that lets you concentrate on your text. Despite its minimalistic approach, the application offers a variety of distinctive features that are specifically designed for writing-focused tasks. With iA Writer, you can gain unparalleled insight into your writing structure, detect superfluous words and clichés, and navigate through your documents and notes with ease.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There is no other writing tool in the world better than iA Writer for me. It&amp;rsquo;s the one and only tool I use when writing long-form content (including this one of course) or when extreme focus is needed.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s fast, stylish, and unobtrusive. Something about this tool makes you feel like you are connecting with the words, and nothing else matters.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t know what I am talking about, you should try it.&lt;/p&gt;
&lt;h2 id=&#34;dayone&#34;&gt;DayOne&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://dayoneapp.com&#34;&gt;https://dayoneapp.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t write journal everyday. But I still find the joy whenever I re-read the memories I&amp;rsquo;ve captured. It&amp;rsquo;s &lt;a href=&#34;https://www.inkandswitch.com/local-first/&#34;&gt;local-first&lt;/a&gt; software, simple and elegant.&lt;/p&gt;
&lt;p&gt;Even if I use paper notebooks for many journals, I still frequently use DayOne, especially when I&amp;rsquo;ve had great times with the kids, so I can include pictures or even voice recordings.&lt;/p&gt;
&lt;h2 id=&#34;syncthing&#34;&gt;Syncthing&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://syncthing.net&#34;&gt;https://syncthing.net&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Syncthing is a continuous file synchronization program. It synchronizes files between two or more computers in real time, safely protected from prying eyes. Your data is your data alone and you deserve to choose where it is stored, whether it is shared with some third party, and how it’s transmitted over the internet.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This tool is the unsung hero in the background. It&amp;rsquo;s a P2P, reliable, fast syncing tool.&lt;/p&gt;
&lt;p&gt;I use it to synchronize folders among multiple macOS devices. I normally setup a special folder that allows me to share things between my work and personal laptops. I&amp;rsquo;ve never encountered a single issue. It even worked flawlessly when I traveled back to China and tried to sync things from my Mac mini back in US.&lt;/p&gt;
&lt;h2 id=&#34;logseq&#34;&gt;Logseq&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://logseq.com&#34;&gt;https://logseq.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Logseq is another &lt;a href=&#34;https://www.inkandswitch.com/local-first/&#34;&gt;local-first&lt;/a&gt; outliner-styled note-taking software. It took me a while to embrace its outlining nature. After using it for a year, I&amp;rsquo;ve grown to love its outline aspects. This limitation genuinely forces me to think more clearly and coherently.&lt;/p&gt;
&lt;p&gt;You can link notes or ideas from each other, and even make PDF notes right inside Logseq. Best of all, all data is stored locally in your selected folder in Markdown (or Org) formats.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Tools are a personal thing, things works for me might not get the love from others, which is fine.
Keep exploring and creating.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Minimal setup time for your new project</title>
      <link>https://orchardlab.dev/posts/foundation/</link>
      <pubDate>Fri, 25 Aug 2023 15:29:43 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/foundation/</guid>
      <description>&lt;p&gt;I remember the early days of Rails when people often criticized the Java world, or the J2EE world to be more specific, for being too complex and over-engineered, even for simple projects.&lt;/p&gt;
&lt;p&gt;Slowly, the whole industry is now shifting more towards bootstrapping, being lean, and scrappy.&lt;/p&gt;
&lt;p&gt;Any time people try to set up a solid foundation, they are accused of wasting time; any time people try to create things with scalability in mind, they&amp;rsquo;re labeled as over-engineering.&lt;/p&gt;
&lt;p&gt;The truth is, a solid foundation is not over-engineering; its constraints help you focus on the problem, which can actually increase your creativity. A good foundation is about eliminating unnecessary options; it&amp;rsquo;s about focusing on doing things in a limited number of ways—sometimes, the only way.&lt;/p&gt;
&lt;p&gt;Returning to the Rails example, yes, it&amp;rsquo;s opinionated, but there is a well-established pattern for doing certain things. Controllers, actions, models, and templates are all the tools we have. Try to only use these tools and start creating.&lt;/p&gt;
&lt;p&gt;Another example is React. Compared to other frameworks, its success largely stems from its limitations—the very few ways of putting pixels onto the screen.&lt;/p&gt;
&lt;p&gt;A good, solid setup at the beginning is not a waste of time; it saves time both in the short term and long term. In the short term, it boosts your confidence and unleashes your creativity; in the long term, it ensures that your idea can still work.&lt;/p&gt;
&lt;p&gt;When starting a new project, try to set aside time for minimal foundational setup. Do not gloss over this important step.&lt;/p&gt;
&lt;p&gt;It can take you further.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>The Future Computers</title>
      <link>https://orchardlab.dev/posts/the-future-computers/</link>
      <pubDate>Mon, 05 Jun 2023 18:02:40 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/the-future-computers/</guid>
      <description>&lt;p&gt;Such a great read on Jason Fried&amp;rsquo;s &lt;a href=&#34;https://world.hey.com/jason/two-visions-of-the-future-88e4d9bf&#34;&gt;Two visions of the future&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Though, I am not sure how I feel about either of them.&lt;/p&gt;
&lt;p&gt;Sometimes I wish there were more easy to use UIs to get things done. Sometimes I wish all I needed to deal with was text, instead of learning all kinds of fancy UIs from your designers.&lt;/p&gt;
&lt;p&gt;The best computers are the ones that disappear or blend into your life. This is even often mentioned in Apple&amp;rsquo;s own marketing.&lt;/p&gt;
&lt;p&gt;I appreciate the silence of Apple computers, from the early Mac mini to today&amp;rsquo;s Apple silicon computers. From this sense, it&amp;rsquo;s fantastic to be able to work without thinking about computers.&lt;/p&gt;
&lt;p&gt;On the other hand, this VR thing straps a whole bunch of UIs in front of my eyes.&lt;/p&gt;
&lt;p&gt;I think VR would be great for meditation, for games, for emerging experiences. Which really is about as little UI as possible, but rather trying to create whole different environments.&lt;/p&gt;
&lt;p&gt;But staring at large rectangular &amp;ldquo;windows&amp;rdquo; by strapping on a device? Maybe not. At least for me.&lt;/p&gt;
&lt;p&gt;Oh, and for now, I would be leaning towards a more headless (UI-less) future.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>April 15 2023</title>
      <link>https://orchardlab.dev/quotes/04-15-2023/</link>
      <pubDate>Sat, 15 Apr 2023 09:10:11 -0700</pubDate>
      
      <guid>https://orchardlab.dev/quotes/04-15-2023/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;We [at Apple] feel that, for some crazy reason, we’re in the right place at the right time to put something back. And what I mean by that is, most of us didn’t make the clothes we’re wearing, and we didn’t cook or grow the food that we eat, and we’re speaking a language that was developed by other people, and we use a mathematics that was developed by other people. We are constantly taking.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;And the ability to put something back into the pool of human experience is extremely neat. I think that everyone knows that in the next ten years we have the chance to really do that. And we [will] look back—and while we’re doing it, it’s pretty fun, too—we will look back and say, “God, we were a part of that!”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;We started with nothing. So whenever you start with nothing, you can always shoot for the moon. You have nothing to lose. And the thing that happens is—when you sort of get something, it’s very easy to go into cover-your-ass mode, and then you become conservative and vote for Ronnie. So what we’re trying to do is to realize the very amazing time that we’re in and not go into that mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href=&#34;https://book.stevejobsarchive.com&#34;&gt;Steve Jobs Archive&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>April 10 2023</title>
      <link>https://orchardlab.dev/quotes/04-10-2023/</link>
      <pubDate>Mon, 10 Apr 2023 08:39:11 -0700</pubDate>
      
      <guid>https://orchardlab.dev/quotes/04-10-2023/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Art is about the maker&lt;/p&gt;
&lt;p&gt;Its aim: to be an expression of who you are.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;You are creating the work that best represents you.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Wanting to outperform another artist or make a work better than theirs rarely results in true greatness. Nor is it a mindset that has a healthy impact on the rest of our lives. As Theodore Roosevelt pointed out, comparison is the thief of joy. Besides, why would we want to create with the purpose of diminishing someone else?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;Great art is an invitation, calling to creators everywhere to strive for still higher and deeper levels.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rick Rubin&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/60965426-the-creative-act&#34;&gt;&lt;em&gt;The Creative Act: A Way Of Being&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>April 08 2023</title>
      <link>https://orchardlab.dev/quotes/04-08-2023/</link>
      <pubDate>Sat, 08 Apr 2023 10:35:28 -0700</pubDate>
      
      <guid>https://orchardlab.dev/quotes/04-08-2023/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;If we can tune in to the idea&lt;/p&gt;
&lt;p&gt;of making things and sharing them&lt;/p&gt;
&lt;p&gt;without being attached to the outcome,&lt;/p&gt;
&lt;p&gt;the work is more likely&lt;/p&gt;
&lt;p&gt;to arrive in its truest form.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rick Rubin&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.goodreads.com/book/show/60965426-the-creative-act&#34;&gt;&lt;em&gt;The Creative Act: A Way Of Being&lt;/em&gt;&lt;/a&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Griffith Observatory, LA</title>
      <link>https://orchardlab.dev/photos/griffith-observatory/</link>
      <pubDate>Sat, 08 Apr 2023 10:30:03 -0700</pubDate>
      
      <guid>https://orchardlab.dev/photos/griffith-observatory/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/photos/griffith-observatory/29CDC7E1.jpeg&#34; alt=&#34;&#34;&gt;
10/30/2022&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Weir Canyon Trail</title>
      <link>https://orchardlab.dev/photos/weir-canyon-trail/</link>
      <pubDate>Sat, 08 Apr 2023 10:30:03 -0700</pubDate>
      
      <guid>https://orchardlab.dev/photos/weir-canyon-trail/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/photos/weir-canyon-trail/80D273A5.jpeg&#34; alt=&#34;&#34;&gt;
3/26/2023&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/photos/weir-canyon-trail/DA107260.jpeg&#34; alt=&#34;&#34;&gt;
3/26/2023&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>First article using Bamboo 🎋</title>
      <link>https://orchardlab.dev/posts/first-article-using-bamboo/</link>
      <pubDate>Sun, 15 Jan 2023 16:13:08 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/first-article-using-bamboo/</guid>
      <description>&lt;p&gt;I am really tired of the unnecessary ceremonies of writing a new article using my &lt;a href=&#34;https://gohugo.io&#34;&gt;beloved&lt;/a&gt; static site generator:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;open terminal&lt;/li&gt;
&lt;li&gt;find the folder&lt;/li&gt;
&lt;li&gt;create a new file with the title&lt;/li&gt;
&lt;li&gt;open editor to log down my thoughts&lt;/li&gt;
&lt;li&gt;&lt;code&gt;git add&lt;/code&gt;, &lt;code&gt;git commit&lt;/code&gt; and &lt;code&gt;git push&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of doing all of these, how about something which you can just &lt;strong&gt;open&lt;/strong&gt;, &lt;strong&gt;write&lt;/strong&gt; and &lt;strong&gt;publish&lt;/strong&gt; (while still using the greatest static generator).&lt;/p&gt;
&lt;p&gt;Yeah, that&amp;rsquo;s Bamboo 🎋, a little macOS native app I am working on so I can write more in 2023 ✍️.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Next Journey</title>
      <link>https://orchardlab.dev/posts/next-journey/</link>
      <pubDate>Fri, 02 Dec 2022 17:10:20 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/next-journey/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/connect.jpg&#34; alt=&#34;connect&#34;&gt;&lt;/p&gt;
&lt;p&gt;I &lt;a href=&#34;https://orchardlab.dev/posts/journey-at-amazon/&#34;&gt;promised&lt;/a&gt; to share what will be my next journey. But before that, I want to share 3 personal stories.&lt;/p&gt;
&lt;h2 id=&#34;story-1&#34;&gt;Story 1&lt;/h2&gt;
&lt;p&gt;In 2015, while I was living in Foster City Bay Area, I met my neighbor Robin. At that time, I started running on and off, but unfortunately in the every possible wrong way (this simple sport seems not simple at all). Turns out he&amp;rsquo;s a seasoned ultra and sub-3 marathon runner, most importantly, he&amp;rsquo;s patient and willing to guide me through.&lt;/p&gt;
&lt;p&gt;He invited me to the BURN running community, started coaching me on how to run the first marathon (The longest distance I ran before that was 5k). He made plans for my 3 months training, check-in after every hard workout, helped me through the injured times and how to recover from it.&lt;/p&gt;
&lt;p&gt;Eventually, in the December CIM (California International Marathon) race, he paced me the whole race, and I finished in 3 hours 17 minutes. I can&amp;rsquo;t even possibly think of finishing 26.2 miles for the first year of running, not even to mention under 3:30!&lt;/p&gt;
&lt;h2 id=&#34;story-2&#34;&gt;Story 2&lt;/h2&gt;
&lt;p&gt;At the beginning of this year. My son Aaron saw I had a brand-new notebook (LEUCHTTURM 1917) sitting on my bookshelf. He asked me whether he can have it. I thought this would be a great way to nudge him to start journaling, also an opportunity for me to journal more often (which I only do one or two times per week). So I created a little challenge with him.&lt;/p&gt;
&lt;p&gt;The rule is if he can journal 30 days in a streak, then the notebook would be his; at the same time, I will start journaling with him as well, if I skip any day, the notebook will also belong to him. So while giving me some motivation, it also makes the challenge a little more fun.&lt;/p&gt;
&lt;p&gt;Fast-forward to now, Aaron finished his 30 days streak (even on his sick days), he is still journaling now and then when finding interesting things or back from a family trip, he got his notebook of course. For me, I &lt;strong&gt;journaled every single day&lt;/strong&gt; since that. Its Aaron helped me “installed” this micro habit which I don&amp;rsquo;t even need to think about it before bed now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/aaron-journal.jpeg&#34; alt=&#34;Aaron not feeling good journal&#34; title=&#34;Aaron&#39;s journal on one of his sick days&#34;&gt;&lt;/p&gt;
&lt;h2 id=&#34;story-3&#34;&gt;Story 3&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;ve always wanted to start blogging again (last time was still in college). But always put it off for all kinds of reasons. In James Clear&amp;rsquo;s book &lt;a href=&#34;https://jamesclear.com/atomic-habits&#34;&gt;Atomic Habits&lt;/a&gt;, he shared that he&amp;rsquo;s making bets with his friends to encourage himself to build the habits. So I thought to give it a try. Then I sent this message to my friend &lt;a href=&#34;https://www.linkedin.com/in/j4lin/&#34;&gt;Jason&lt;/a&gt; on Telegram on Jan 25, 2022.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I will write 5 articles by end of Feb 10, and published it. Otherwise I will give Jason Lin 1000 dollars.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Yes, I finished 5 articles before Feb 10 (Cause this guy published the message to LinkedIn, which turns it into a thing not about money but pride).&lt;/p&gt;
&lt;p&gt;But I still vividly remember, after the 3rd article, I felt like running out of steam. I don&amp;rsquo;t think I would have finished the last 2 posts if there weren&amp;rsquo;t someone out there expecting the writings from me (and the 1000 dollars, and the post).&lt;/p&gt;
&lt;h2 id=&#34;connecting-the-dots&#34;&gt;Connecting the dots&lt;/h2&gt;
&lt;p&gt;Maybe these stories seem random to you, but to me, they are all connected, showing the importance of human relations, the importance of even simple message check-ins with real human beings once in a while.&lt;/p&gt;
&lt;p&gt;As people working in software and technology, all the time, often &lt;em&gt;over-estimate the power of machines and under-estimate the power of human beings&lt;/em&gt;. At least that&amp;rsquo;s something blinded me for such a long time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Forgot about all the apps, websites, fancy tools, it&amp;rsquo;s only taking another human being to transform yourself, even if (especially) if it&amp;rsquo;s a kid.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I am happy to start my next journey at &lt;a href=&#34;https://www.betterup.com&#34;&gt;BetterUp&lt;/a&gt;, which I believe, could help more people transform themselves through other human beings. No matter if BetterUp can success or not, but at least, we can redirect some attention to human relationships, not just technologies.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Journey at Amazon</title>
      <link>https://orchardlab.dev/posts/journey-at-amazon/</link>
      <pubDate>Thu, 01 Dec 2022 15:05:12 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/journey-at-amazon/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve never imagined that I could stay at one company for almost 8 years, let alone Amazon.&lt;/p&gt;
&lt;p&gt;But here I am, last week I finished my longest career tenure at Amazon. It was a grateful, intensive yet fulfilling journey.&lt;/p&gt;
&lt;p&gt;Over the years, I&amp;rsquo;ve realized that how much time you spent on one place is not about that company, but more about people around you. I felt so lucky to have a great(best) &lt;a href=&#34;https://www.linkedin.com/in/krishnakirang/&#34;&gt;manager&lt;/a&gt;, a stable and caring team and lots of super smart and talented people there. We tackle one problem after another, creating new initiatives, cancel projects, start new ones. All those ups and downs with the team are just a valuable experience for me. Those people constructed my whole working and growth experiences, those experiences and opportunities shaped who I am. It gave me new perspectives on seeing the path ahead of me.&lt;/p&gt;
&lt;p&gt;I am so grateful for working with my hiring manager (&lt;a href=&#34;https://www.linkedin.com/in/krishnakirang/&#34;&gt;Krishna G&lt;/a&gt;) for about 6 years. While I was still hesitating joining the team back in 2014, he said to me over the phone: &amp;ldquo;You will not be bored here.&amp;rdquo;. Ok ok, I cheated a bit, he also said to me &amp;ldquo;You will not be doing on-call&amp;rdquo;. But anyway, it turns out both of what he said to me were true, I got the opportunity to do dedicated front-end work and improve a lot of our customer and developer experiences, and I had a intensive and yet very enjoyable fulfilling experiences solving all kinds of problems. He&amp;rsquo;s always trying to motivate me to find the next thing to create/improve to make our customers happier while leveraging my own interests, most importantly, giving me the freedom to do so. I once shared in my yearly review that &lt;em&gt;&amp;ldquo;Krishna is like my charger, who can always find ways to boost my energy level to 100%.&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I always tell me friends that don&amp;rsquo;t expect learning secret technologies at Amazon (I am sure there are of course), cause that&amp;rsquo;s not the point. The secret of Amazon is all about &lt;strong&gt;learning&lt;/strong&gt; and &lt;strong&gt;exercising&lt;/strong&gt; the &lt;a href=&#34;https://www.aboutamazon.com/about-us/leadership-principles&#34;&gt;LPs&lt;/a&gt; (Leadership Principles). I am grateful that I got the chance to really understand and breathe the meaning of them in the day-to-day work. All the technical choices, product initiatives, ideas, innovations start with LPs. By arming with those north-star values plus the relentless emphasis on writing (which is really just critical-thinking), making Amazon a unique place to be.&lt;/p&gt;
&lt;p&gt;Like all other journeys, you will encounter a forking point someday. It&amp;rsquo;s not about how anything wrong with the current trail, but the fork leading to the new path just seems so much more attractive and aligning with my believes and core values &lt;strong&gt;better&lt;/strong&gt;. Which I will share more in the next post.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Championships, coaching and zen</title>
      <link>https://orchardlab.dev/posts/championships-coaching-zen/</link>
      <pubDate>Thu, 10 Nov 2022 10:28:43 -0800</pubDate>
      
      <guid>https://orchardlab.dev/posts/championships-coaching-zen/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/eleven-rings.jpeg&#34; alt=&#34;eleven-rings&#34;&gt;&lt;/p&gt;
&lt;p&gt;There are books which its title can&amp;rsquo;t justify the greatness of its content.&lt;/p&gt;
&lt;p&gt;The legendary coach &lt;a href=&#34;https://en.wikipedia.org/wiki/Phil_Jackson&#34;&gt;Phil Jackson&lt;/a&gt;&amp;rsquo;s book &lt;em&gt;Eleven Rings - The Soul of Success&lt;/em&gt; is definitely one of those.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve known about Phil Jackson since middle school while watching NBA playoffs of Bulls and Lakers, he had coached the best basketball players like Micheal Jordan and Kobe Bryant, lead Bulls and Lakers to win &lt;strong&gt;11&lt;/strong&gt; NBA championships across his entire coaching career. And yes I know at that time people called him Zen Master. Of course, I had zero idea or interest on what&amp;rsquo;s that even mean when I was a teenager back then.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve always been put off by those kinds of books with &amp;ldquo;success&amp;rdquo; in the title, at the same time though, I&amp;rsquo;ve also attracted by the books with &amp;ldquo;soul&amp;rdquo; in the title. Mostly because I&amp;rsquo;ve been holding the idea of in-directness, or achieving things in the natural way too tight. Seeking success without meaningfulness to me seems unnatural and uncomfortable. But eventually I decided to pick up this book, it turns out that every bit of it is as natural as possible on leaderships, coaching and self actualization.&lt;/p&gt;
&lt;p&gt;As a software engineer, I&amp;rsquo;ve been more and more realized that the importance of coaching, the relationships played to achieve your self actualization. During my past 15 years of career, I&amp;rsquo;ve had the most significant satisfaction and growth in the past 8 years at Amazon. I would attribute that to my manager, my mentor and friend &lt;a href=&#34;https://www.krishnakiran.com&#34;&gt;Krishna G&lt;/a&gt; without a blink. &lt;strong&gt;Great manager or mentor are the ones helping you to find your own purpose and meaning to reach your actualization.&lt;/strong&gt; Looking back, there are so many overlaps between Phil Jackson and Krishna in terms of coaching philosophy, making someone to become better themselves while contributing to the greater goals of the team. I feels extremely lucky being able to find the harmony working and delivering with a manager/teacher like him.&lt;/p&gt;
&lt;p&gt;Maybe that&amp;rsquo;s one of the reason why I find this book resonate with me such given my experiences with Krishna.&lt;/p&gt;
&lt;p&gt;We all often put too much attention on the heroic individuals, the super stars, the one man changes everything.&lt;/p&gt;
&lt;p&gt;After reading the book, I had much more admiration and respects on Phil Jackson&amp;rsquo;s ability to accelerate Micheal Jordan and Kobe Bryant&amp;rsquo;s process of becoming a leader, enabling them to get the bigger achievements than before. Most importantly, to expedite the process of those super star&amp;rsquo;s actualization. Somehow, it made me feels the super stars are &amp;ldquo;less magic&amp;rdquo; than before.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve learned so many from the book about finding the internal drive of working, about managing angers, about leadership, about team building, about finding your inner peace, about how to making choices, about achieving your self actualization. It&amp;rsquo;s a combination of the last second shot excitement and spiritual Zen-ness philosophies.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s philosophical while down to the earth.&lt;/p&gt;
&lt;p&gt;I feel lucky that I opened the book and happy to find the reason  why some of my experiences makes me happy or sad. Above all, it makes me cherish more about the people I am working with, the current moment.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Beginners Mind</title>
      <link>https://orchardlab.dev/posts/beginners-mind/</link>
      <pubDate>Thu, 06 Oct 2022 16:18:02 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/beginners-mind/</guid>
      <description>&lt;p&gt;Sometimes, not knowing something exists is a blessing.&lt;/p&gt;
&lt;p&gt;How many times have you got discouraged once you know your &amp;ldquo;&lt;em&gt;awesome idea&lt;/em&gt;&amp;rdquo; already being implemented by others, maybe also better in every other ways than yours as well.&lt;/p&gt;
&lt;p&gt;When I was a junior software engineer, I always hope I could&amp;rsquo;ve known more. Hopping that I could&amp;rsquo;ve known there are already solutions to the problems in my head in all kinds of forms. So that I don&amp;rsquo;t need to re-invent the wheel.&lt;/p&gt;
&lt;p&gt;After doing software development for a while (little more than 15 years), also being a customer of using all kinds of software. I realized one thing: &lt;em&gt;No matter how simple and ubiquitous other software behaves and looks. Turns out, the thing you build will almost always slightly different than others.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;My point is, people choose using different tools for lots of reasons, sometimes for certain functionalities, sometimes for certain UX, sometimes even just for the joyfulness the tool sparks.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Every single tool has a different taste in it. Because the human who made them are different.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Never stop creating. Don&amp;rsquo;t be afraid of re-inventing the wheel, especially in the software domain.&lt;/p&gt;
&lt;p&gt;Best part is even if you somehow produced something exactly as someone else already built (which is very unlikely), the process of turning something from 0 to 1 will well worth it.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>The Missing Helper for Your Static Blogs - Part 1</title>
      <link>https://orchardlab.dev/posts/the-missing-helper-for-your-static-blogs/</link>
      <pubDate>Thu, 06 Oct 2022 15:36:11 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/the-missing-helper-for-your-static-blogs/</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Notes: This is a documentation-driven-development series I am currently experimenting. So most likely the code doesn&amp;rsquo;t exists yet before publishing this article. What I am hoping is that even if the project eventually can not be finished, the idea could still live in somewhere.🤞🏼&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Static site generators are a blessing. Since &lt;a href=&#34;https://jekyllrb.com/&#34;&gt;Jekyll&lt;/a&gt; and Github pages, the world had been moving towards static sites in big strides. To say the least, personal blogs are among the majority of them.&lt;/p&gt;
&lt;p&gt;Static site generators are great in so many ways, the best parts probably is the &lt;em&gt;1. endless customizability and&lt;/em&gt; &lt;em&gt;2. content first.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;However, there comes a cost. Thinking about the process you writing a blog post:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open &lt;a href=&#34;https://iterm2.com/&#34;&gt;iTerm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd ~/sites/orchardlabdev-site&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hugo new content/posts/i-have-something-to-share.md&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;code .&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Locate the new file and start writing.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Not to mention the specialized tools we are using here, to start writing something, we would need to do 5 steps. Of course you can have some shortcuts to streamline this process. But then in the writing mode, if you want to put a picture, you would need to drag it drop to a certain folder then grab the name and link it correctly in the markdown file. &lt;strong&gt;So much frictions to write!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So I am wondering, can we have a little helper doing some of these (if not all of them) for us? Something can make us writes more, think more, instead of fiddling the tools and facing 10 options. I would love to have something like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;open a link&lt;/li&gt;
&lt;li&gt;start writing&lt;/li&gt;
&lt;li&gt;Drag and drop will automatically put the images in the right folder and insert the image link as well&lt;/li&gt;
&lt;li&gt;After all done I can publish (which commits with message provided and then push to remote).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Can we build something like this? So that&amp;rsquo;s my plan.&lt;/p&gt;
&lt;h2 id=&#34;shape-of-the-tool&#34;&gt;Shape of the tool&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;I would like the tool to be a small binary. Which I can fire up in the static site folder.&lt;/li&gt;
&lt;li&gt;It can automatically detect the static site generator according to the folder structure (Hugo or Jekyll).&lt;/li&gt;
&lt;li&gt;The main editing interface would be a web UI which we can:
&lt;ul&gt;
&lt;li&gt;Write&lt;/li&gt;
&lt;li&gt;Edit&lt;/li&gt;
&lt;li&gt;Publish&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;(Optional) Basic authentication of the web UI. This is not required at all at the beginning but would be nice later on if we want to deploy it to the cloud.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&#34;choosing-the-tools&#34;&gt;Choosing the tools&lt;/h2&gt;
&lt;p&gt;For the binary I would like to use Rust, cause that&amp;rsquo;s a good one if you want your tool to show up on the HackerNews front page :-). Kidding aside, I think Rust is great at building cross-platform binary tools, and it&amp;rsquo;s fast too.&lt;/p&gt;
&lt;p&gt;For the Web UI, I will use Svelte.&lt;/p&gt;
&lt;h2 id=&#34;high-level-design&#34;&gt;High level design&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;File based. It&amp;rsquo;s all file based, meaning the file on the disk is the single source of truth. There is no database evolved at all.&lt;/li&gt;
&lt;li&gt;Rust will read the local folder structure and convert them into domain models.&lt;/li&gt;
&lt;li&gt;Key domain models here would be posts which correspond to the local markdown files&lt;/li&gt;
&lt;li&gt;Each post will have the common metadata cross different static site generator tools
&lt;ul&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;author&lt;/li&gt;
&lt;li&gt;published at&lt;/li&gt;
&lt;li&gt;content&lt;/li&gt;
&lt;li&gt;layout (this can be configured / changed)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Folders contains the markdown files will be called collection&lt;/li&gt;
&lt;li&gt;New posts will automatically goes into the collection (if there is only one), otherwise user can choose where the default writing goes to&lt;/li&gt;
&lt;li&gt;Rust exposed RESTful API for our Svelte Web UI to use
&lt;ul&gt;
&lt;li&gt;List collections&lt;/li&gt;
&lt;li&gt;List posts among each collection&lt;/li&gt;
&lt;li&gt;Create new post (markdown file)&lt;/li&gt;
&lt;li&gt;Save/update new post (markdown file)&lt;/li&gt;
&lt;li&gt;Delete a post (markdown file)&lt;/li&gt;
&lt;li&gt;Publish (which will trigger local git commit)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since I am currently using Hugo, so I would like to support Hugo first and then increase the scope of the tool to others like Jekyll or 11ty etc.&lt;/p&gt;
&lt;h2 id=&#34;whats-next&#34;&gt;What&amp;rsquo;s next&lt;/h2&gt;
&lt;p&gt;Next we will be start building the core logics using Rust first. See you next post.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Software Sometimes Is Annoying</title>
      <link>https://orchardlab.dev/posts/software-sometimes-is-annoying/</link>
      <pubDate>Sat, 16 Apr 2022 10:25:35 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/software-sometimes-is-annoying/</guid>
      <description>&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/tesla-uneven-tire-wear.jpeg&#34; alt=&#34;tesla-uneven-tire-wear&#34;&gt;&lt;/p&gt;
&lt;p&gt;This is the start of the story. Unfortunately, this is the end of the story too.&lt;/p&gt;
&lt;p&gt;About two months ago, this the above screen popped up in my Tesla. I thought, well, look how smart this car is. It can even tell me the tire&amp;rsquo;s wear conditions without using &lt;a href=&#34;https://www.wikihow.com/Check-Tire-Tread&#34;&gt;coins or special tools&lt;/a&gt; to measure. Software is amazing!&lt;/p&gt;
&lt;p&gt;I took the car to the dealer, as expected, they replaced two brand new tires on the rear side, since the front two tires are still pretty decent, so they kept it there (to save me some money). Awesome, brand new tires, peace of mind.&lt;/p&gt;
&lt;p&gt;The only problem is, the warning still stays there. Because the rear side is too new, so the difference between the front and the rear is still big enough than the software&amp;rsquo;s configuration. And there is no local reset or override option in the car&amp;rsquo;s software system itself, this is controlled by the &amp;ldquo;&lt;em&gt;centralized&lt;/em&gt;&amp;rdquo; configuration in the Tesla software. The service person explained to me that the other Tesla team was well aware of this issue and working a fix in the future software updates, but they (dealers) have no way to fix it.&lt;/p&gt;
&lt;p&gt;The upside is that this could be fixed automatically over the wire through a software updates. The downside is it might takes a long time or forever.&lt;/p&gt;
&lt;p&gt;As a software engineer, a change might not be that strait -forward than it looks. Yes, it&amp;rsquo;s a configuration change, but I guess we can&amp;rsquo;t just change the configuration globally (again I am not a car person, so maybe what I am going to say here is totally rubbish). There will be discussions around should we allow local overrides or not, how much buffer should we allow, how do we expose this local overrides, shall we only allow technician to do it or any car owner to do it. Once we finalized the design for the change, we would allocate resources to design the UI and put the work in the pipeline. Anything more important happened could again de-prioritize this and move it into the backlog.&lt;/p&gt;
&lt;p&gt;Everyone has a different opinion regarding to the importance of a software fix. From customer&amp;rsquo;s perspective, any feature we rely on is worth the fix. But what bothers me the most is the  &amp;ldquo;false positive&amp;rdquo; signal it shows (all the time).&lt;/p&gt;
&lt;p&gt;Because of this always on warning on the dash, after couple of weeks of driving, it numb my brain to pay any attention to any warnings anymore. Cause I will by default treat anything showing up there as &amp;ldquo;false positive&amp;rdquo;. Depending on the person, it might make some people more ignorance or more stressful.&lt;/p&gt;
&lt;p&gt;I can&amp;rsquo;t remember even once in the past that I sent my car to the dealer to clear warnings in the dash ended up they are saying: &amp;ldquo;Sorry our software teams are working on it, we can&amp;rsquo;t do anything about it now.&amp;rdquo; As a software engineer, this makes me sad. I can feel even a simple decision we made might makes some users miserable using it.&lt;/p&gt;
&lt;p&gt;Software in a way empowers people to have more customizations than the analog world, but at the same time, it also makes us feels helpless by using it. Why is that?&lt;/p&gt;
&lt;p&gt;I think there are so many factors involved. There is no single one or two things 100% determined the way software has to be. I will just share some of my incomplete thinkings around this.&lt;/p&gt;
&lt;p&gt;I think one of the reason is the centralization, software becomes more and more centralized, there is only one team working on this significant feature which applies to every car running on the road. &lt;strong&gt;Every company eventually becomes a software company.&lt;/strong&gt; People design car softwares in California might rarely think of people driving it everyday &lt;a href=&#34;https://twitter.com/marcoarment/status/1477738618861068294&#34;&gt;in the freezing places&lt;/a&gt;. &lt;em&gt;They are designed centralized while distributed globally.&lt;/em&gt; Why it has to be centralized and uniformed? Because it&amp;rsquo;s cheaper to do so.&lt;/p&gt;
&lt;p&gt;Another reason I think is the lacking of standard or componentized structure. Replacing a compatible wheel or tire is way more strait-forward than replacing a piece of software component. Because most of the time, tangible things are an open book while the software is a black-box. From this perspective, I think &lt;a href=&#34;https://webassembly.org/&#34;&gt;WebAssembly&lt;/a&gt; might help in the future software design, especially for things with interchangeable components. Yes, there is for sure no silver bullet.&lt;/p&gt;
&lt;p&gt;I am a huge fan of &lt;a href=&#34;https://dynamicland.org/&#34;&gt;Dynamicland&lt;/a&gt;. One thing I interpreted is that it exposes the black-box software to the outside. And enable people to change, transform and collaborate on top of it. At the same time, software enables the dynamism and make the regular object like a living thing. I don&amp;rsquo;t know if and how this could applies to the car software, but I would be more than happy if it can enable me to get rid of that annoying warnings.&lt;/p&gt;
</description>
    </item>
    
    
    
    
    
    <item>
      <title>Thick-client software is coming back ?</title>
      <link>https://orchardlab.dev/posts/local-first-is-becoming-a-feature/</link>
      <pubDate>Mon, 11 Apr 2022 17:33:59 -0700</pubDate>
      
      <guid>https://orchardlab.dev/posts/local-first-is-becoming-a-feature/</guid>
      <description>&lt;p&gt;We are living in a world of saturated SaaS products. There is always a service for that. Every single tool we&amp;rsquo;ve used to install on our machine now have a version in the cloud. From notes taking (Notion) to photo editing (Adobe) to designing tools (Figma) to even coding (VS code in the cloud). I think it&amp;rsquo;s fair to say, SaaS services pushes the &lt;em&gt;instant gratification of getting things done&lt;/em&gt; to the limit. By the end of the day, all you need is a browser and a URL.&lt;/p&gt;
&lt;p&gt;Of course this is not the complete story. Along with this convenience, you also have to accept couple of sacrifices as well. The always on connection, losing (at least part of) privacy, and dependency of the running service are among the most notable ones.&lt;/p&gt;
&lt;p&gt;I can see there is an interesting &lt;strong&gt;come back&lt;/strong&gt; of the original &lt;strong&gt;thick client&lt;/strong&gt; software coming back. But this time is different than the prior web 2.0 days. Before web 2.0 we are doing &lt;strong&gt;thick client&lt;/strong&gt; because of low performance of the browser or the lacking of interactivity of the web.&lt;/p&gt;
&lt;p&gt;This time however, &lt;strong&gt;thick client&lt;/strong&gt; or another cool term &lt;a href=&#34;https://www.inkandswitch.com/local-first/&#34;&gt;Local-first software&lt;/a&gt; is mainly caused by privacy concerns, decentralization of the future. It&amp;rsquo;s not all about performance, although that matters as well. But mainly we want something to be firstly working on my local machine, then we want that cloud-enabled ability to collaborate as well.&lt;/p&gt;
&lt;p&gt;Please remember local-first doesn&amp;rsquo;t mean it&amp;rsquo;s built using native technology, although that&amp;rsquo;s the natural choice because local means close the native platform it provides. We can still leveraging web tools or browser to develop local-first software as well. As a web developer, I am particularly interested in building offline first software using web-assembly recently. It enables us to reach most of the people while not losing too much on the native performance.&lt;/p&gt;
&lt;p&gt;Yes, there will be tons of people are willing to make all of these three sacrifices given it&amp;rsquo;s so compelling to just pay and forget about it. Software at the end of day, maybe to most of the people, is just a tool to get things done, to make some zeros to ones or vice versa. Also local first software might not applying to everything. For example, it doesn&amp;rsquo;t make too much sense to make a food ordering service offline first. But I think local-first software will shine the most on creativity and some productivity or privacy focused areas. Yes, this is what I will bet on and personally want it to happen.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Deployment is part of developer experiences</title>
      <link>https://orchardlab.dev/posts/2022-03-30-deploy-your-software/</link>
      <pubDate>Wed, 30 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-30-deploy-your-software/</guid>
      <description>&lt;h3 id=&#34;story-1&#34;&gt;Story 1&lt;/h3&gt;
&lt;p&gt;Back in 2005 when I was still in college. Friends found me a part time job developing a desktop application for a barber shop. .NET and C# pretty much on the headlines news everyday, it&amp;rsquo;s also a modern way of writing desktop application at that time. So I choose C# to development the application.&lt;/p&gt;
&lt;p&gt;Microsoft makes the development process as easy as drag and drop controls and then hooking your database business code. I had so much fun of doing the development.&lt;/p&gt;
&lt;p&gt;By the time of the deployment though, it gave me a hard time. Because every machine needs to have the correct version of .NET runtime installed, and a SQL server instance running at least somewhere in the network as well. It&amp;rsquo;s been a nightmare for me to have it working on the client&amp;rsquo;s machine.&lt;/p&gt;
&lt;h3 id=&#34;story-2&#34;&gt;Story 2&lt;/h3&gt;
&lt;p&gt;Fast forward to 2011. I was super excited about Ruby and Ruby on Rails, I got my first Mac. Another time, I got a contract work to develop an expense system for a big company in China.&lt;/p&gt;
&lt;p&gt;Of course I am gonna use Ruby on Rails, cause there is nothing at that time beat the &lt;strong&gt;developer experience&lt;/strong&gt; of that. While I am having all the fun and learnings having the web application built. I realized that the client can only allow windows operating system running at their company. Anyone who familiar with the early days of getting &lt;em&gt;Ruby on Rails&lt;/em&gt; running on a windows machine should know what I am talking about. It took weeks to finally get it up running on that machine, not only that, every new deployment is like another big unknown for me. Because of the huge trouble of that, they finally &lt;strong&gt;canceled&lt;/strong&gt; the entire project and looking for someone else who can build them a ASP.net application.&lt;/p&gt;
&lt;h3 id=&#34;deployment-focused&#34;&gt;Deployment focused&lt;/h3&gt;
&lt;p&gt;Over the years, I&amp;rsquo;ve been very much appreciating the programming languages with the mind of &lt;em&gt;easy of deployment&lt;/em&gt;. Languages like Rust or Go just makes the running your software on other people&amp;rsquo;s machine dead easy.&lt;/p&gt;
&lt;h3 id=&#34;customer-mindset&#34;&gt;Customer mindset&lt;/h3&gt;
&lt;p&gt;As a user, I would be way much happier if it&amp;rsquo;s a &lt;strong&gt;single binary&lt;/strong&gt; rather than 200 files scattered all over the place. That&amp;rsquo;s why I am huge fan of tools like &lt;a href=&#34;https://syncthing.net/&#34;&gt;&lt;strong&gt;syncthing&lt;/strong&gt; - files synching tool&lt;/a&gt; and &lt;a href=&#34;https://github.com/m3ng9i/ran&#34;&gt;&lt;strong&gt;ran&lt;/strong&gt; - local http server&lt;/a&gt;. I think that&amp;rsquo;s also a reason I favor Mac over Windows because of the single &lt;strong&gt;.app&lt;/strong&gt; application file.&lt;/p&gt;
&lt;h3 id=&#34;spas---self-contained-program&#34;&gt;SPAs - self contained program&lt;/h3&gt;
&lt;p&gt;This might be sounds like a stretch argument, but I think &lt;a href=&#34;https://en.wikipedia.org/wiki/Single-page_application&#34;&gt;&lt;strong&gt;Single-page application&lt;/strong&gt;&lt;/a&gt; to some extend is an easier (might not be better, but that&amp;rsquo;s totally different discussion) way to distribute client side UI than traditional server side rendering apps.&lt;/p&gt;
&lt;p&gt;Couple years ago, we want to migrate a huge production running web application from an old JSP (customized Java server) to a modern Spring based to mitigate some security risks. At that time the UI is written in a SPA framework, the Java server vended APIs and using JSP for the layout only. No matter how much I dislike client side rendering or the specific SPA framework they chosen at that time (spoiler: not React), I really appreciate &lt;em&gt;the self-contained nature of SPAs&lt;/em&gt;. The migration of the backend is super smooth.&lt;/p&gt;
&lt;h3 id=&#34;long-lasting&#34;&gt;Long lasting&lt;/h3&gt;
&lt;p&gt;I&amp;rsquo;ve written quite some side projects over the years, interesting thing is the one wrote using Go or pure client side JavaScript are still running perfectly fine, but the ones written in Ruby or Node can&amp;rsquo;t even pass the &lt;code&gt;bundle&lt;/code&gt; or &lt;code&gt;npm install&lt;/code&gt; phase.&lt;/p&gt;
&lt;p&gt;To end this already long article, I guess all I am trying to say is &lt;em&gt;treating software deployment as part of the crafting. Cause &lt;a href=&#34;https://en.wikipedia.org/wiki/Steve_Jobs#cite_note-Insanely_Great-143&#34;&gt;real artists ship&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Don&#39;t just follow</title>
      <link>https://orchardlab.dev/posts/2022-03-28-dont-just-follow/</link>
      <pubDate>Mon, 28 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-28-dont-just-follow/</guid>
      <description>&lt;p&gt;Someone sent me a CR the other day. And the code went something like this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;validateInputLanguage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;input&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;InputOptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;c1&#34;&gt;// checking input is valid
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;  &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;// ....
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;c1&#34;&gt;// we are good
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;else&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
    &lt;span class=&#34;nx&#34;&gt;input&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;language&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;defaultLang&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The code here doesn&amp;rsquo;t matter that much though. I read the CR, put the following comments:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would recommend to not mutate passed input, instead of returning the validation result plus reasons in the return value (instead of void).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then the engineer relied to me this in the Slack:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Hi Mike, I saw your comments. I just follow what the original code patterns, the other places did the same kind of validations as well.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Every time people replied something like this, they basically shut down a door to ask why.&lt;/p&gt;
&lt;p&gt;Yes, I understand there could be other places like this, but what if we start asking questions instead of just trying to get the job done?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Why did we make such pattern in the first place?&lt;/li&gt;
&lt;li&gt;Does this pattern makes sense?&lt;/li&gt;
&lt;li&gt;Is there any other better ways to achieve the same thing?&lt;/li&gt;
&lt;li&gt;Is that risky to change the pattern now?&lt;/li&gt;
&lt;li&gt;How we can reduce the risks to break things but still can make the code slightly better than before?&lt;/li&gt;
&lt;li&gt;If now is not the right time, when do you think that time will be?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Come on, we all have things to do, why bother asking so many questions?&lt;/p&gt;
&lt;p&gt;Every code change ultimately is an exercise for our brain, or a hidden conversation to the original author. Even though we could still ended up making the same code change, but with these questions in mind will help you making a bigger impact in the long term.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Pattern matching in TypeScript using ts-pattern</title>
      <link>https://orchardlab.dev/posts/2022-03-22-pattern-matching-using-ts-pattern/</link>
      <pubDate>Tue, 22 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-22-pattern-matching-using-ts-pattern/</guid>
      <description>&lt;p&gt;While I &lt;a href=&#34;https://orchardlab.dev/posts/2022-03-12-rss-feeds-parser-and-core-models/&#34; title=&#34;About Us&#34;&gt;mentioned&lt;/a&gt; how great to do pattern match in Elixir. It&amp;rsquo;s surprisingly good to do this in TypeScript thanks to the &lt;a href=&#34;https://github.com/gvergnaud/ts-pattern&#34;&gt;ts-pattern&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here is what I did to grab the content of different RSS feeds using Elixir:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; 
    &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; 
    &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; 
    &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;description&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; 
    &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;content_html&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content_html&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content_html&lt;/span&gt;
&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;No Content&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And this is the &lt;code&gt;ts-pattern&lt;/code&gt; version:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;kr&#34;&gt;type&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;RSSItemShape&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
  &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content_html&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;};&lt;/span&gt;
  
&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;extractContent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;RSSItemShape&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;item&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content_html&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;exhaustive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First of all I should say this might looks a little like comparing Apple to Orange. Because Elixir&amp;rsquo;s pattern matching is a language feature, while the pattern matching of TypeScript is to leveraging the &lt;code&gt;ts-pattern&lt;/code&gt; library. The performance of Elixir&amp;rsquo;s version is most likely better than &lt;code&gt;ts-pattern&lt;/code&gt; until someday this was built into V8 node engine, I don&amp;rsquo;t need to worry the Elixir&amp;rsquo;s pattern matching won&amp;rsquo;t work in the future unless syntax changes. But I would need to keep an eye on the version upgrades of &lt;code&gt;ts-pattern&lt;/code&gt; which might introduce non-backward compatible changes.&lt;/p&gt;
&lt;p&gt;However, today I am just gonna compare it from the developer&amp;rsquo;s experience perspective, how would it helps us to achieve our goals, express our intention more efficiently.&lt;/p&gt;
&lt;p&gt;The first thing I noticed is that both the Elixir version and &lt;code&gt;ts-pattern&lt;/code&gt; version are pretty concise. Yes Elixir has more flexibility that you can do the pattern matching on function level or block level. But I think &lt;code&gt;ts-pattern&lt;/code&gt; did a great job in terms of readability. Unlike Elixir can absorb this into the language design,  &lt;code&gt;ts-pattern&lt;/code&gt; has to be creative within the JavaScript language syntax boundary. But &lt;code&gt;ts-pattern&lt;/code&gt; already did a great job by boiling it down to the minimal &lt;strong&gt;key words:&lt;/strong&gt; &lt;code&gt;match&lt;/code&gt;, &lt;code&gt;with&lt;/code&gt;, &lt;code&gt;otherwise&lt;/code&gt; and &lt;code&gt;exhaustive&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Composable&lt;/strong&gt;. The one lovely thing about &lt;code&gt;ts-pattern&lt;/code&gt; is the composable due to its method calls. Composing multiple pattern matching can be quite handy and strait-forward in &lt;code&gt;ts-pattern&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Last thing of course is the &lt;strong&gt;type safety&lt;/strong&gt; &lt;code&gt;ts-pattern&lt;/code&gt; can give you. It won&amp;rsquo;t compile if you are missing cases when using the &lt;code&gt;exhaustive&lt;/code&gt; keyword. This could prevent some errors creeping into the runtime. Be careful when passing &lt;code&gt;any&lt;/code&gt; object into the &lt;code&gt;match&lt;/code&gt;, as &lt;code&gt;ts-pattern&lt;/code&gt; has no way to know if you covered all the cases or not. So it will only throw exception during the runtime.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-typescript&#34; data-lang=&#34;typescript&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// matching any object
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;extractContent&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;any&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;null&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;match&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;result&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;res&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;kd&#34;&gt;with&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;content_html&lt;/span&gt;: &lt;span class=&#34;kt&#34;&gt;__.string&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;},&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;r&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;content_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;exhaustive&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt; &lt;span class=&#34;c1&#34;&gt;// This will not prevent compile time check as we are matching any object
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Overall I am super happy with &lt;code&gt;ts-pattern&lt;/code&gt;, hopefully it can make into JavaScript feature someday, as TypeScript essentially &lt;a href=&#34;https://github.com/Microsoft/TypeScript/issues/165&#34;&gt;constrained&lt;/a&gt; by the target language poses.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>SPAs nudges us to caring more UX</title>
      <link>https://orchardlab.dev/posts/2022-03-18-spa-is-not-a-mistake/</link>
      <pubDate>Fri, 18 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-18-spa-is-not-a-mistake/</guid>
      <description>&lt;p&gt;I still vividly remember the days when &lt;a href=&#34;%22https://backbonejs.org/%22&#34;&gt;BackboneJS&lt;/a&gt; came out, it might not the first Single Page Application framework, but probably the most notable one at that time. Before that, I was doing lots of Rails development for building web apps. BackboneJS for sure gave me a feel of fresh air when developing web UIs. Everything related to UI just became so lightweight to develop. Yes we would need to start thinking extra things which were covered by the browser in the past, things like manually handle form submission, URL redirects using JavaScript which is built-in into the browser already.&lt;/p&gt;
&lt;p&gt;But there is the upside, like everything else. It&amp;rsquo;s the tendency to create more customer experience focused product, it nudges you to think more about the experience, instead of your beautiful data. I am not saying SPA will inherently have a better UX than MPA, that&amp;rsquo;s also what so many people are criticizing SPA for, saying they ruined the web, which I can&amp;rsquo;t disagree more. Just look at the advancement we made over HTML5, JavaScript and CSS, it&amp;rsquo;s hard to imagine if we are still rendering each web page statically on the server side using JSP, are we gonna get any of these? It&amp;rsquo;s exactly because we are keep pushing the boundaries using rich client side computing, creating madness things on the web pushes these web standards forward. Somewhat like the game industry did to the computer hardwares.&lt;/p&gt;
&lt;p&gt;But maybe we are doing way too much on the client side using JavaScript? There is a trend in the recent years that to move most of the things if not everything &lt;a href=&#34;%22https://github.com/readme/featured/server-side-languages-for-front-end%22&#34;&gt;back to server&lt;/a&gt;. At first I was one of them cheering for this as well, because of the insane complexity of the front end space. Until one day I was trying to do a tab switch using web socket to change the state on the server side. Wait a second, there is something wrong here. As a user, I am suddenly scared of knowing that everything I do on my screen were processed and scrutinized on the server.&lt;/p&gt;
&lt;p&gt;Not because it&amp;rsquo;s posting the privacy issue by piggy back to servers every single click (it does so too), the problem I am concerned is we setup ourself again to be further away with the users. I&amp;rsquo;ve been doing a lots of front end only development and mixing both, at last I found that it&amp;rsquo;s extremely hard to build something really great by thinking everything in the backend context. Dealing with data vs dealing with human interaction is fundamentally different. I admire people can do both well at the same time.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;%22https://matt-rickard.com/spas-werent-a-mistake/%22&#34;&gt;SPAs aren&amp;rsquo;t a mistake&lt;/a&gt;. It&amp;rsquo;s something in the history for the first time reminds us: your code doesn&amp;rsquo;t matter if it can&amp;rsquo;t express nicely in a human friendly form.&lt;/p&gt;
&lt;p&gt;We as human like to go to extremes, because it&amp;rsquo;s so much easier to do so. It&amp;rsquo;s hard to calibrate, to pick, to combine, to make the best of both worlds. Though I think we are &lt;a href=&#34;%22https://www.youtube.com/watch?v=860d8usGC0o%22&#34;&gt;gradually&lt;/a&gt; &lt;a href=&#34;%22https://kit.svelte.dev/%22&#34;&gt;reaching&lt;/a&gt; &lt;a href=&#34;%22https://nextjs.org/%22&#34;&gt;there&lt;/a&gt;. Oh, also don&amp;rsquo;t forget, doing everything on the browser sometimes can be &lt;a href=&#34;%22https://www.nytimes.com/games/wordle/index.html%22&#34;&gt;quite beautiful&lt;/a&gt;.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>RSS feeds parser and core models</title>
      <link>https://orchardlab.dev/posts/2022-03-12-rss-feeds-parser-and-core-models/</link>
      <pubDate>Sat, 12 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-12-rss-feeds-parser-and-core-models/</guid>
      <description>&lt;h2 id=&#34;parser&#34;&gt;Parser&lt;/h2&gt;
&lt;p&gt;There are so many RSS parser out there. I&amp;rsquo;ve tried lots of them, including JavaScript, Elixir and Rust versions. So far the most reliable one I&amp;rsquo;ve tested is the Rust version from &lt;a href=&#34;https://github.com/rust-syndication&#34;&gt;
rust-syndication&lt;/a&gt;. There is even a Elixir &lt;a href=&#34;https://github.com/avencera/fast_rss&#34;&gt;fast_rss&lt;/a&gt; wrapped on top of the &lt;a href=&#34;https://lib.rs/crates/rss&#34;&gt;rss&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The wrapper is pretty lightweight &lt;a href=&#34;https://github.com/rusterlium/rustler&#34;&gt;rustler&lt;/a&gt; makes the whole interop with Rust with Elixir so much easier and straightforward.&lt;/p&gt;
&lt;p&gt;I could&amp;rsquo;ve just use &lt;a href=&#34;https://github.com/avencera/fast_rss&#34;&gt;fast_rss&lt;/a&gt;, but I also want to parse Atom feeds as well. So I ended up took the approach of &lt;code&gt;fast_rss&lt;/code&gt; and build another wrapper around atom feed parser on top of Rust&amp;rsquo;s &lt;a href=&#34;https://github.com/rust-syndication/atom&#34;&gt;atom&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This is what the Rust code wrapper &lt;a href=&#34;https://github.com/29decibel/feedex/blob/44a3e6c642404c9548f39f5f54f60510ad7f3283/native/feedex_native/src/lib.rs&#34;&gt;looks like&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&#34;core-models&#34;&gt;Core models&lt;/h2&gt;
&lt;p&gt;Once we have the RSS and Atom feed parser, along with JSON feed. We would ended up with a map data structure of the feeds. Since there are so many stuff in the RSS feeds, all I want for now is just part (title, date, content, author) of that. So I would like to be working from the backwards. Start from what the UI might looks like, then define the core models to support this UI.&lt;/p&gt;
&lt;p&gt;I took a screenshot of an awesome app called &lt;a href=&#34;https://netnewswire.com/&#34;&gt;NetNewsWire&lt;/a&gt; (You should try it if you haven&amp;rsquo;t).&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/highlighted-netnewswire.png&#34; alt=&#34;netnewswire-screenshot&#34;&gt;&lt;/p&gt;
&lt;p&gt;By looking at the UI, here you can find the minimal information we would need to extract from the RSS feed.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;kd&#34;&gt;defmodule&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Item&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;kd&#34;&gt;defstruct&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;date&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;authors&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;defmodule&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Feed&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;kd&#34;&gt;defstruct&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;link&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;title&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;favicon&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;no&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;items&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[]&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These two models by no means are the final version, but it&amp;rsquo;s a good start for us to start to create our minimal RSS reader UI. Once we have the model in mind, the fun part of leveraging the power of Elixir begins.&lt;/p&gt;
&lt;h2 id=&#34;pattern-matching-to-extract&#34;&gt;Pattern matching to extract&lt;/h2&gt;
&lt;p&gt;Okay, so now we have both the core models and the parsed RSS/Atom feeds in Elixir&amp;rsquo;s map format. Time to normalize/extract the key contents into our defined core models above. I think that&amp;rsquo;s where the Elixir&amp;rsquo;s pattern matching really shines.&lt;/p&gt;
&lt;p&gt;For example, we would like to extract the content of the feed item. For different rss versions, the place they put the content will be different. Here are a couple of examples and its Elixir extraction function:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://orchardlab.dev/assets/elixir-pattern-matching-extract-rss-content.svg&#34; alt=&#34;elixir-pattern-matching-extract-rss-content&#34;&gt;&lt;/p&gt;
&lt;p&gt;The entire content field extracting including error case handling (nothing can be found) can boils down to this:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt;
      &lt;span class=&#34;s2&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;%{&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
    &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;content&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; 
    &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt;
      &lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;%{&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;value&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
    &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;value&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;description&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;})&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_binary&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
  &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;description&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(%{&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;content_html&amp;#34;&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content_html&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;content_html&lt;/span&gt;

&lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;get_item_content&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;_&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;No Content&amp;#34;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;Note: &lt;code&gt;is_binary&lt;/code&gt; here is basically trying to check if the given value is a string. You can read more here at Elixir&amp;rsquo;s doc: &lt;a href=&#34;https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html&#34;&gt;https://elixir-lang.org/getting-started/binaries-strings-and-char-lists.html&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I don&amp;rsquo;t know about you. But I found &lt;strong&gt;extremely satisfying writing pattern matching&lt;/strong&gt; comparing to nested &lt;code&gt;if/else&lt;/code&gt; or &lt;a href=&#34;https://en.wikipedia.org/wiki/Guard_%28computer_science%29&#34;&gt;guard statement&lt;/a&gt;. I think mostly because how human&amp;rsquo;s brain works, we are exceptionally good at &lt;a href=&#34;https://en.wikipedia.org/wiki/Pattern_recognition_(psychology)&#34;&gt;pattern matching&lt;/a&gt;. Instead of one step at a time checking x then y, we &lt;strong&gt;describe&lt;/strong&gt; what the &lt;strong&gt;stuff&lt;/strong&gt; we want out of the given shape of data.&lt;/p&gt;
&lt;p&gt;Here you can find the all the &lt;a href=&#34;https://github.com/29decibel/feedex/blob/44a3e6c642404c9548f39f5f54f60510ad7f3283/lib/feedex/feed_extractor.ex&#34;&gt;data extraction&lt;/a&gt; related code. Please notice this project is still being actively developed.&lt;/p&gt;
&lt;p&gt;Now we actually have a minimum working version of given an feed url, parsing and extracting them into a the desired data model we want. Next, we will see how can leverage the part we already have so far to make something useful.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Create a RSS reader and beyond in 2022</title>
      <link>https://orchardlab.dev/posts/2022-03-11-create-a-rss-reader-and-beyond/</link>
      <pubDate>Fri, 11 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-11-create-a-rss-reader-and-beyond/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been using RSS reader for almost more than 15 years. Right start from graduated from college. It&amp;rsquo;s amazed to see all kinds of different forms product on top of this open (while also chaotic) format.&lt;/p&gt;
&lt;p&gt;There are native apps, AI based, open source web apps. We&amp;rsquo;ve never been at a time lacking of RSS readers. While bother creating another one?&lt;/p&gt;
&lt;p&gt;First of all, I love working on open data. Same reason why I love the open web so much. You don&amp;rsquo;t need to get permission in order to scrape a webpage (unless you commercialize the product which is a different topic), you don&amp;rsquo;t need permission in order to write a MonkeyScript to make the web page looks like what you wanted. It&amp;rsquo;s the librating and freedom this platform enabled attracts me so much.&lt;/p&gt;
&lt;p&gt;Second, I always want a little bit different here and there for the RSS reader I wanted. For example, I want a different theme or font in order to focus reading a long article, I want to highlight some parts or write something about it, sometimes I also want the reader just generate a PDF offline for me without open the webpage, I also want to read the full article of some feeds while they somehow configured it just to read the first paragraph.&lt;/p&gt;
&lt;p&gt;Last but not least, I am hoping to create something beyond just a RSS reader, something a little bit bigger scope for my online readings, like read it later.&lt;/p&gt;
&lt;p&gt;Ultimately, it&amp;rsquo;s the experience of building such thing matters the most.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Use Svelte in Phoenix the simple way</title>
      <link>https://orchardlab.dev/posts/2022-03-08-phoenix-svelte-the-simple-way/</link>
      <pubDate>Tue, 08 Mar 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-03-08-phoenix-svelte-the-simple-way/</guid>
      <description>&lt;h2 id=&#34;tldr&#34;&gt;TLDR&lt;/h2&gt;
&lt;p&gt;&lt;a href=&#34;https://gist.github.com/29decibel/5d8443d5121232bc1033aec53c2fc9cc&#34;&gt;Gist link for the changes to make this happen&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&#34;why&#34;&gt;Why&lt;/h2&gt;
&lt;p&gt;While there were few options to integrate Svelte into existing Phoenix app. But I still don&amp;rsquo;t find any of them to be particular simple. What I really want is something that I can understand, as minimal as possible, no heavy Node tooling involved. After play with different options, I finally found a sweet spot for me to use Svelte while not overwhelmed by the additional toolings. In fact, it&amp;rsquo;s just couple lines of configuration with two files both less than 20 lines of code.&lt;/p&gt;
&lt;p&gt;Before diving into my solution to this. Let&amp;rsquo;s first understand how Phoenix integrate with JavaScript/CSS assets by default.&lt;/p&gt;
&lt;h2 id=&#34;turn-svelte-components-into-js&#34;&gt;Turn Svelte components into JS&lt;/h2&gt;
&lt;p&gt;Since 1.6, Phoenix uses esbuild to build your JavaScripts. The esbuild is a standalone Go program thus no Node, NPM invovled. That&amp;rsquo;s why in the &lt;code&gt;assets&lt;/code&gt; folder, there is neither &lt;code&gt;package.json&lt;/code&gt; nor &lt;code&gt;node_modules&lt;/code&gt; . The esbuild will slurp the entry &lt;code&gt;app.js&lt;/code&gt; then output the results into &lt;code&gt;priv/static/assets&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;So the first thought I have is how can I not step away from the default path, still using esbuild for Svelte. The good news is that there is a esbuild-svelte plugin, the bad news is that I have to invoke the plugin asynchronously, in other words, by writing a JS build file.&lt;/p&gt;
&lt;p&gt;Install packages:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;nb&#34;&gt;cd&lt;/span&gt; assets &lt;span class=&#34;o&#34;&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm install esbuild esbuild-svelte svelte svelte-preprocess --save-dev
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Luckly, esbuild-svelte already handled the heavy lifting part. So the build.js is actually quite simple:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// build.mjs
&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;esbuild&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;esbuild&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;esbuildSvelte&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;esbuild-svelte&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sveltePreprocess&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;svelte-preprocess&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
&lt;span class=&#34;kr&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;readdir&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;fs/promises&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;

&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allSvelteComponents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;baseDir&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;./js/app/&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;readdir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;baseDir&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;all&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;filter&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;endsWith&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;.svelte&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)).&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;((&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;baseDir&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}${&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;f&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sb&#34;&gt;`&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;

&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;async&lt;/span&gt; &lt;span class=&#34;kd&#34;&gt;function&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;components&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;kr&#34;&gt;await&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;allSvelteComponents&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;
  &lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isProd&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;env&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;NODE_ENV&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;production&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;
  &lt;span class=&#34;nx&#34;&gt;esbuild&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;build&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;entryPoints&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;components&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;format&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;esm&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;bundle&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;true&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;minify&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;isProd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;watch&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;isProd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;outdir&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;../priv/static/assets/svelte&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
      &lt;span class=&#34;nx&#34;&gt;plugins&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
        &lt;span class=&#34;nx&#34;&gt;esbuildSvelte&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;({&lt;/span&gt;
          &lt;span class=&#34;nx&#34;&gt;preprocess&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;sveltePreprocess&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(),&lt;/span&gt;
        &lt;span class=&#34;p&#34;&gt;}),&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;],&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;})&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;catch&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(()&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;process&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;exit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Using &lt;code&gt;.mjs&lt;/code&gt; as extension here to indicate this is a ES module. So that I don&amp;rsquo;t need to change the &lt;code&gt;package.json&lt;/code&gt;&amp;rsquo;s type globally.&lt;/p&gt;
&lt;p&gt;Now if you manually do &lt;code&gt;node ./assets/build.mjs&lt;/code&gt; you should be able to see the built components JS in the &lt;code&gt;priv/static/assets/svelte&lt;/code&gt; folder.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s add this build step into both dev and deployment time:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# mix.exs&lt;/span&gt;
 &lt;span class=&#34;kd&#34;&gt;defp&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;aliases&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
      &lt;span class=&#34;s2&#34;&gt;&amp;#34;assets.deploy&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
        &lt;span class=&#34;n&#34;&gt;...&lt;/span&gt;
        &lt;span class=&#34;c1&#34;&gt;# build svelte components&lt;/span&gt;
        &lt;span class=&#34;s2&#34;&gt;&amp;#34;cmd --cd assets NODE_ENV=production node ./build.mjs&amp;#34;&lt;/span&gt;
      &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# dev.exs&lt;/span&gt;
&lt;span class=&#34;ss&#34;&gt;watchers&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;...&lt;/span&gt;
    &lt;span class=&#34;c1&#34;&gt;# build and watch svelte components&lt;/span&gt;
    &lt;span class=&#34;ss&#34;&gt;node&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;build.mjs&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;--watch&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;cd&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Path&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;expand&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;../assets&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;__DIR__&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)]&lt;/span&gt;
  &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is it.&lt;/p&gt;
&lt;h2 id=&#34;consuming-it-through-script-typemodule&#34;&gt;Consuming it through &lt;code&gt;&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script type=&amp;quot;module&amp;quot;&amp;gt;&lt;/code&gt; is such a wonderful thing. It finally makes consuming JavaScript so much easier and nicer.&lt;/p&gt;
&lt;p&gt;Now all you need to do to render a Svelte component is put this in your &lt;code&gt;.heex&lt;/code&gt; template:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;hello&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&#34;p&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;script&lt;/span&gt; &lt;span class=&#34;na&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;module&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;&amp;gt;&lt;/span&gt;
    import Hello from &amp;#34;&lt;span class=&#34;err&#34;&gt;&amp;lt;&lt;/span&gt;%= Routes.static_path(@conn, &amp;#34;/assets/svelte/Hello.js&amp;#34;) %&amp;gt;&amp;#34;
    new Hello({
        target: document.getElementById(&amp;#34;#hello&amp;#34;),
        props: { name: &amp;#34;Mike&amp;#34; }
    })
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is it.&lt;/p&gt;
&lt;h2 id=&#34;render-it-the-elixir-way&#34;&gt;Render it the Elixir way&lt;/h2&gt;
&lt;p&gt;To push it a bit further. You can write such a helper to make the above rendering even simpler.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-elixir&#34; data-lang=&#34;elixir&#34;&gt;&lt;span class=&#34;kd&#34;&gt;defmodule&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;EasySvelteExampleWeb.SvelteView&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
  &lt;span class=&#34;kn&#34;&gt;use&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;EasySvelteExample&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:view&lt;/span&gt;

  &lt;span class=&#34;c1&#34;&gt;# just for default values&lt;/span&gt;
  &lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;props&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;\\&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[])&lt;/span&gt;

  &lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_list&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;render_component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;into&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;%{}))&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;kd&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;render_component&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;ow&#34;&gt;when&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;is_map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;props&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;component_id&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;svelte-ele-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;-&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;component_import_path&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Routes&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;static_path&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;/assets/svelte/&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;.js&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;props_json&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;props&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Jason&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;encode!&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;
    &lt;span class=&#34;n&#34;&gt;component_class_name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;name&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;split&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;-&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map_join&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;String&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;capitalize&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ni&#34;&gt;&amp;amp;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt;

    &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
      &lt;span class=&#34;n&#34;&gt;content_tag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;ss&#34;&gt;:div&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;component_id&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
      &lt;span class=&#34;n&#34;&gt;content_tag&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;:script&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;type&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;module&amp;#34;&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
        &lt;span class=&#34;sh&#34;&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;        import &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;component_class_name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt; from &amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;component_import_path&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;&amp;#34;
&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;        new &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;component_class_name&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;({
&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;            target: document.getElementById(&amp;#34;&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;component_id&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;&amp;#34;),
&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;            props: &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;#{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;props_json&lt;/span&gt;&lt;span class=&#34;si&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;
&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;        })
&lt;/span&gt;&lt;span class=&#34;sh&#34;&gt;        &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
        &lt;span class=&#34;o&#34;&gt;|&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;raw&lt;/span&gt;
      &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
    &lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;

  &lt;span class=&#34;kd&#34;&gt;defp&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;random_id&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;do&lt;/span&gt;
    &lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;_&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;..&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;into&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;ss&#34;&gt;do&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;nc&#34;&gt;Enum&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;random&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;0123456789abcdef&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&amp;gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;span class=&#34;k&#34;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now to render a svelte component in &lt;code&gt;.heex&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-fallback&#34; data-lang=&#34;fallback&#34;&gt;&amp;lt;%= SvelteView.render_component(&amp;#34;Hello&amp;#34;, name: &amp;#34;Mike&amp;#34;) %&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here you can find the &lt;a href=&#34;https://gist.github.com/29decibel/5d8443d5121232bc1033aec53c2fc9cc&#34;&gt;gist link&lt;/a&gt; of all the changes needed for this.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Re-watch Rethinking reactivity</title>
      <link>https://orchardlab.dev/posts/2022-02-11-rewatch-rethinking-reactivity/</link>
      <pubDate>Fri, 11 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-02-11-rewatch-rethinking-reactivity/</guid>
      <description>&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=AdNJ3fydeao&#34;&gt;https://www.youtube.com/watch?v=AdNJ3fydeao&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Wether or not you are a front end developer or even a software engineer. This talk worth your time either from a inspirational perspective on how can we create more accessible things, or from a marketing perspective, I&amp;rsquo;ve never saw a better &amp;ldquo;marketing talk&amp;rdquo; for a front end framework, watching this talk constantly reminds me of &lt;a href=&#34;https://www.youtube.com/watch?v=5sMBhDv4sik&#34;&gt;Apple&lt;/a&gt; or &lt;a href=&#34;https://vimeo.com/221553894&#34;&gt;Volvo&lt;/a&gt;&amp;rsquo;s advertisement.&lt;/p&gt;
&lt;h2 id=&#34;reactivity-the-easy-way&#34;&gt;Reactivity, the easy way&lt;/h2&gt;
&lt;p&gt;Svelte is more like &lt;a href=&#34;https://gist.github.com/Rich-Harris/0f910048478c2a6505d1c32185b61934&#34;&gt;a language&lt;/a&gt; other than a framework. It&amp;rsquo;s like a modern dialect of the web, combined all three of HTML, CSS and JavaScript in one single file. The most interesting part is the compiler behind it, which tracks the &amp;ldquo;changes&amp;rdquo; of your variables. The magics lies into the assignment tracking, for example when you do &lt;code&gt;foo = 42&lt;/code&gt;, anywhere uses &lt;code&gt;foo&lt;/code&gt; will be evaluated thus you get the &amp;ldquo;automatic&amp;rdquo; reactivity of the interface without needing to do anything manually.&lt;/p&gt;
&lt;p&gt;Reactivity in JavaScript land is not new, &lt;a href=&#34;https://rxjs.dev/&#34;&gt;RxJS&lt;/a&gt; has been there for a long time. But thinking in terms of streams is quiet some learning curve or adaption for programers, while assignment is almost the first day we learned from programming 101.&lt;/p&gt;
&lt;h2 id=&#34;compiler&#34;&gt;Compiler&lt;/h2&gt;
&lt;p&gt;The other key part of the Svelte is the compiler. There is a trend that we are doing more and more in the back scene instead of shipping large frameworks. The compiler enables us to still write in the way that we used to, but free from the limitation of given languages, cause the compiler will help &amp;ldquo;translate&amp;rdquo; them into &amp;ldquo;low level&amp;rdquo; bits.&lt;/p&gt;
&lt;p&gt;It enables us to think more in the human friendly way, instead of forcing us to adapting our thinking approach in the machine way.&lt;/p&gt;
&lt;p&gt;Ultimately, it&amp;rsquo;s a decoupling and abstraction. So the compiler could improve our web app performance without us changing our existing code.&lt;/p&gt;
&lt;p&gt;Another advantage or the compiler is to produce more optimized bundle depending on the environment. Whether it would be JavaScript script on the client side, or plain string on the server side.&lt;/p&gt;
&lt;p&gt;All of these are the great parts, but what touched me the most is the non-technical parts.&lt;/p&gt;
&lt;h2 id=&#34;accessible-tool&#34;&gt;Accessible tool&lt;/h2&gt;
&lt;p&gt;I am always fascinated about the non-technical parts for certain tools or technology, in other words, &lt;em&gt;the beliefs of the creator&lt;/em&gt; behind the scene. One reason might be the &amp;ldquo;fear&amp;rdquo; that I have over the loved technology might change, because even though the technology or the implementation changes, the thinking or main idea behind it rarely do.&lt;/p&gt;
&lt;p&gt;Another reason is I think ideas simply lasts longer than specific technology. Just like &lt;a href=&#34;https://en.wikipedia.org/wiki/Dynabook&#34;&gt;Dynabook&lt;/a&gt; did to iPads, &lt;a href=&#34;https://en.wikipedia.org/wiki/Smalltalk&#34;&gt;Smalltalk&lt;/a&gt; did to the OOP (though Alan Kay means &lt;a href=&#34;http://userpage.fu-berlin.de/~ram/pub/pub_jf47ht81Ht/doc_kay_oop_en&#34;&gt;quite differently things&lt;/a&gt;) and a lot &lt;a href=&#34;https://www.notion.so/blog/data-model-behind-notion&#34;&gt;other ideas&lt;/a&gt; influenced and shaped &lt;a href=&#34;https://www.notion.so/&#34;&gt;Notion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If I have to summarize what&amp;rsquo;s fundamentally different Svelte is different than other front end frameworks. It&amp;rsquo;s &lt;strong&gt;accessibility&lt;/strong&gt;. Here in the context I didn&amp;rsquo;t mean the &lt;a href=&#34;https://www.w3.org/WAI/fundamentals/accessibility-intro/&#34;&gt;Web Accessibility&lt;/a&gt; though it&amp;rsquo;s super well &lt;a href=&#34;https://svelte.dev/docs#accessibility-warnings&#34;&gt;considered and covered&lt;/a&gt; in Svelte already.&lt;/p&gt;
&lt;p&gt;Here I am more talking about &lt;strong&gt;Svelte is more accessible to use in order to create web interactive UI&lt;/strong&gt;. HTML is no doubt is the most accessible pillar of three web elements(HTML, CSS, JavaScript). By bringing back the focus to the HTML markup while adding interactivity is a tremendously mental reliving comparing to popular front end frameworks like React which is almost exclusively using JavaScript to create everything.&lt;/p&gt;
&lt;p&gt;Rich mentioned his inspiration came from his wife using spreadsheets to calculate, analysis complicated business problems.&lt;/p&gt;
&lt;p&gt;I believe tools are fundamentally intend to be used by more and more people, making it less intimidating and more accessible is the key to empower regular people. Apple succeeded not because they looks shinny and beautiful, but because they demystified &lt;a href=&#34;https://en.wikipedia.org/wiki/IMovie&#34;&gt;&amp;ldquo;professional&amp;rdquo; tools&lt;/a&gt; and enables you and me to create as well.&lt;/p&gt;
&lt;p&gt;Which reaches Rich&amp;rsquo;s last words of the talk, which I think it&amp;rsquo;s the essence of Svelte: &lt;strong&gt;In a strange way, it brought us all a little closer together&lt;/strong&gt;.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>What I&#39;ve been missing by using VS Code</title>
      <link>https://orchardlab.dev/posts/2022-02-08-another-look-of-vs-code/</link>
      <pubDate>Tue, 08 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-02-08-another-look-of-vs-code/</guid>
      <description>&lt;p&gt;VS code is awesome, sometimes &lt;a href=&#34;https://copilot.github.com/&#34;&gt;too good&lt;/a&gt;. I used to writing pretty much everything from work to side projects, from Ruby to JavaScript exclusively using Vim. But I noticed that I&amp;rsquo;ve slowly transited using VS Code 90% of the time for the recent years.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been great, it helped me caught lots of bugs because of the smart auto completion, it prevent me from writing so many typos because of the built in spell checking, plus TypeScript almost take over the entire front end development, you never need to worry about code formats, because every save will automatically convert your code into uniformed style through &lt;a href=&#34;https://prettier.io/&#34;&gt;&amp;ldquo;prettier&amp;rdquo;&lt;/a&gt;. The code we wrote feels much more &amp;ldquo;safe&amp;rdquo; and &amp;ldquo;beautiful&amp;rdquo;. In other words, the code we wrote at least from visual perspective looks more like machine produced code.&lt;/p&gt;
&lt;p&gt;The other day, because of VS Code crashes on my machine and refuse to re-opening. I opened the Elixir project using my old friend Vim. Prior to using Vim, I thought I would feel uneasy, cause all the assistant now is gone. But I immediately feels something so familiar before, I feels like I am &lt;strong&gt;start &amp;ldquo;thinking&amp;rdquo;&lt;/strong&gt; again. I feels like I am in control.&lt;/p&gt;
&lt;p&gt;The slogan of Copilot being just a &amp;ldquo;copilot&amp;rdquo; or assistant is an illusion in practice. Once you&amp;rsquo;ve been used to the assistant, you will never be able to truly in control. Every single step of design, every details, variables will be influenced by your assistant. On the positive side, you probably will never be able to coding &amp;ldquo;by yourself&amp;rdquo;, on the negative side, you would never truly feels the flow of coding. It&amp;rsquo;s like riding using with the learner&amp;rsquo;s wheels on, yes it&amp;rsquo;s riding, but no, you didn&amp;rsquo;t know what&amp;rsquo;s riding really feels.&lt;/p&gt;
&lt;p&gt;Programming guru Charles Petzold once wrote about this topic in his essay &lt;a href=&#34;http://charlespetzold.com/etc/DoesVisualStudioRotTheMind.html&#34;&gt;Does Visual Studio Rot the Mind?&lt;/a&gt; back in 2005. One thing resinated me lot is the &amp;ldquo;IntelliSense&amp;rdquo; changes how we think:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;But the implication here is staggering. To get IntelliSense to work right, not only must you code in a bottom-up structure, but within each method or property, you must also write you code linearly from beginning to end — just as if you were using that old DOS line editor, EDLIN. You must define all variables before you use them. No more skipping around in your code.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These intelligent tools are not neutral, they will find every opportunity to change the way how we code, or even worse, how we think.&lt;/p&gt;
&lt;p&gt;We as programmers spent lots of time typing code facing the screen. Because we need to deliver that feature, because we need to cut the latency to half, because we need to increase the conversion to 40%. But there is another side of coding as well.&lt;/p&gt;
&lt;p&gt;Creator of Redis &lt;a href=&#34;http://oldblog.antirez.com/post/redis-manifesto.html&#34;&gt;once said&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Code is like a poem; it&amp;rsquo;s not just something we write to reach some practical result.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you can, open Vim or other plain text editor someday, write some code. Hopefully you will find what you&amp;rsquo;ve been missing all this long.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>How can we use computers for the &#34;good&#34;?</title>
      <link>https://orchardlab.dev/posts/2022-02-07-how-to-use-computers-for-the-good/</link>
      <pubDate>Mon, 07 Feb 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-02-07-how-to-use-computers-for-the-good/</guid>
      <description>&lt;p&gt;When I say &amp;ldquo;good&amp;rdquo; here, I am more talking about the non-utilitarian part. I am not talking about using computer to pay your bills, making an important phone call, or using maps for directions. I am more talking about &amp;ldquo;the others&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;I was touring a school with my son the other day. While we were in the computer class room. I was attracted by all the printed art made by computer programs hanging on the wall.&lt;/p&gt;
&lt;p&gt;Some of them are fine drawing pixel art, some of them are simple shape combinations to form cute animal, some of them are abstractions. This reminds me the very original vision of the computer when I was a kid. You can use computer to make all sorts of things, &lt;strong&gt;to express yourself&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Fast forward to today, when people think about computer, it&amp;rsquo;s social medias, it&amp;rsquo;s Youtube, it&amp;rsquo;s browsing exotic pictures, it&amp;rsquo;s an entertainment device. Part of the reason is because of the internet, part of the reason is the mobile devices. These two things together created &lt;strong&gt;multiple portals&lt;/strong&gt; stay with you all the time. You can escape anytime to a different world, different place all the time. But we almost forget how to create, how to make something simple or beautiful, something fulfilling our creation mind. We were more attached to the &lt;em&gt;outer world&lt;/em&gt;, but less with our &lt;em&gt;inner world&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You probably would argue that computers are just tools, it depends on how you use them. But the problem is today&amp;rsquo;s &lt;strong&gt;computers / operating systems are not neutral devices&lt;/strong&gt;. They almost always emphasize one thing over another, they almost always trying to change how you think about it. If you pay attention to the default applications installed on the Mac OS, you would easily spot the trends. There are now pushing you to sign in iClouds, News, TV apps installed by default, and you can&amp;rsquo;t remove it wether you use it or not, it sits there the first time you start your computer setup. You can hide it, but please never underestimate the power of &lt;a href=&#34;https://blog.codinghorror.com/the-power-of-defaults/&#34;&gt;&lt;strong&gt;defaults&lt;/strong&gt;&lt;/a&gt;. You can also learn more from the awesome book &lt;a href=&#34;https://www.goodreads.com/book/show/3450744-nudge&#34;&gt;Nudge&lt;/a&gt;, it talks extensively on how defaults can make a huge difference to your behavior and life.&lt;/p&gt;
&lt;p&gt;Computers are great on many things, but I still very much love the part that &lt;strong&gt;it enables us to create something we can&amp;rsquo;t create before&lt;/strong&gt;.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Follow people vs follow technology</title>
      <link>https://orchardlab.dev/posts/2022-01-26-follow-people-vs-follow-technoligy/</link>
      <pubDate>Wed, 26 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-01-26-follow-people-vs-follow-technoligy/</guid>
      <description>&lt;p&gt;I&amp;rsquo;ve been a software developer for about 15 years. Along the way, I&amp;rsquo;ve been writing software using different tools and technologies (mixing languages and frameworks in my head): .NET, C#, Ruby (On Rails), JavaScript, Scala, Clojure, Go, Swift, TypeScript, Elixir.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s hard to describe in one or two sentences what I learned along the way from all these languages and frameworks. I sometimes even felt offended by asking listing the languages, frameworks you&amp;rsquo;ve been using in the past. Because I think that missed the point.&lt;/p&gt;
&lt;p&gt;One day I suddenly realized that maybe, asking who had the most impacted to your engineering career is more meaningful than asking what tools or technologies you&amp;rsquo;ve been used. Maybe even more important than what you&amp;rsquo;ve created along the way. Because by creating what, most of the time, is not under our control.&lt;/p&gt;
&lt;p&gt;Things that resinated with me, or &lt;strong&gt;what actually changed my mind or brain, are always people&lt;/strong&gt;, or now people always uses another fancy word &amp;ndash; community.&lt;/p&gt;
&lt;p&gt;I truly understand what design pattern means by learning from &lt;a href=&#34;https://www.bilibili.com/video/BV1kv411x7gz&#34;&gt;Terry Lee&lt;/a&gt; back in 2007, I was ignited by DHH&amp;rsquo;s &lt;a href=&#34;https://www.youtube.com/watch?v=Gzj723LkRJY&#34;&gt;demo&lt;/a&gt; of RubyOnRails in 2008; I was then in the rabbit hole of Ruby programming and Vim by watched all of &lt;a href=&#34;https://twitter.com/garybernhardt&#34;&gt;Gary Bernhardt&lt;/a&gt; &lt;a href=&#34;https://www.destroyallsoftware.com/screencasts&#34;&gt;Destroy All Software&lt;/a&gt;. Then all of the sudden, Rich Hickey and David Nolen came from no where and washed my brain about programming completely. While doing Clojure and ClojureScript programming feels quite satisfying and I created some &lt;a href=&#34;https://github.com/29decibel/assistant&#34;&gt;useless tools&lt;/a&gt; along the way. I was also greatly influenced by &lt;a href=&#34;https://github.com/mbostock&#34;&gt;Mike Bostock&lt;/a&gt; and started thinking the computation medium through building data visualizations at the company.&lt;/p&gt;
&lt;p&gt;At the same time, I somehow feels like my brain is still stuck with Ruby, the simplicity (weirdness) of the syntax just dominant my mind for constructing any programs. So then I re-discovered Elixir. Of course, I&amp;rsquo;ve watched lots of talks by the creator José Valim behind it. Whenever I am coding with Elixir, I feels like I am playing an art piece José made. The &lt;a href=&#34;https://joearms.github.io/published/2013-05-31-a-week-with-elixir.html&#34;&gt;words (highlights are in the comments)&lt;/a&gt; of him also got me deeply.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;That said, I don&amp;rsquo;t know from where you got the impression Elixir is mutable. Or got the impression we are trying to make &amp;ldquo;a better functional language&amp;rdquo;. Yes, it is a functional language, but we never claim it to be a better one (as it is a shallow goal). &amp;ndash; josevalim &lt;a href=&#34;http://disq.us/p/g5ncaz&#34;&gt;original&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Most recently I started following Rich Harries closely. Especially his talk about &lt;a href=&#34;https://www.youtube.com/watch?v=AdNJ3fydeao&#34;&gt;Rethinking Reactivity&lt;/a&gt;. (I will write more about my takeaways on this talk later on). But in nutshell, he encourage us to see the web development in a much simpler and accessible way.&lt;/p&gt;
&lt;p&gt;Ultimately, the tools or languages we are using is made by humans. There are world views implanted into the tools or frameworks, therefore it will eventually &amp;ldquo;get&amp;rdquo; us as well. &lt;strong&gt;By adopting a tool is more like adopting a new perspective of seeing things&lt;/strong&gt;.&lt;/p&gt;
</description>
    </item>
    
    
    
    <item>
      <title>Svelte vs LiveView to my brain</title>
      <link>https://orchardlab.dev/posts/2022-01-25-svelte-vs-liveview-to-my-brain/</link>
      <pubDate>Tue, 25 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://orchardlab.dev/posts/2022-01-25-svelte-vs-liveview-to-my-brain/</guid>
      <description>&lt;p&gt;I have been building a web app product using Phoenix LiveView recently, it&amp;rsquo;s been very pleasant experiences for me. Since I was a long time Ruby (On Rails) developer since 2011 and then got my brain washed on functional programming (Clojure, ClojureScript) in 2014, so Elixir turns out a perfect fit for me (especially my brain). It has the dynamic language feeling but also comes with compiler helps to avoid silly bugs; it has powerful pattern matching; the syntax of the language is simple and concise. I often looking at the code I wrote and felt extremely satisfying, almost to the extent of seeing the code I wrote like an &amp;ldquo;art&amp;rdquo; piece (thanks to Jose on the language design). There are lots of things Elixir changed my brain in the good way that I will write about in a later post. In this one, I would just focus on the LiveView part.&lt;/p&gt;
&lt;p&gt;If you haven&amp;rsquo;t heard LiveView yet, you can read more on &lt;a href=&#34;https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html&#34;&gt;here&lt;/a&gt;. Essentially, I think LiveView is trying to leverage three things: &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/API/WebSocket&#34;&gt;WebSocket&lt;/a&gt;, &lt;a href=&#34;https://www.erlang.org/doc/design_principles/des_princ.html&#34;&gt;OTP&lt;/a&gt; and JS &lt;a href=&#34;https://github.com/patrick-steele-idem/morphdom&#34;&gt;diffing/patching&lt;/a&gt;. Of course this is a dramatically simplified pillars of LiveView, but I think these are the keys if you want to understand what&amp;rsquo;s going behind the scene.&lt;/p&gt;
&lt;p&gt;The first part and arguably the most important part is the web socket, it enables the long live communication to the server, so that any client side events can be &amp;ldquo;think&amp;rdquo; as they are happened directly in your Elixir code as well. Let this idea sink a bit for you before moving on. Because I think this is the fundamental difference not only technically but also can change the way you think about the programs.&lt;/p&gt;
&lt;p&gt;For example, we want to switch a toggle button to change a user profile configuration.&lt;/p&gt;
&lt;p&gt;Here is the traditional flow of simple client/server updates:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user click a button&lt;/li&gt;
&lt;li&gt;send a fetch request to the server&lt;/li&gt;
&lt;li&gt;server set up an endpoint taking the request from client&lt;/li&gt;
&lt;li&gt;server react to that event by doing some database query/updates&lt;/li&gt;
&lt;li&gt;server send the serialized response to client&lt;/li&gt;
&lt;li&gt;client received the response and do things&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;LiveView&amp;rsquo;s flow would be like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user click button&lt;/li&gt;
&lt;li&gt;server side code got the &amp;ldquo;callback&amp;rdquo; event, doing database query/updates&lt;/li&gt;
&lt;li&gt;describe the new shape of the UI&lt;/li&gt;
&lt;li&gt;[user saw the new UI]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Maybe the number of steps in the list can not justify the mental burden it applies to our brain. But here are couple things I would have to think of in the traditional client/server flow which I don&amp;rsquo;t need to in LiveView:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How should I let the client side send the request?&lt;/li&gt;
&lt;li&gt;What endpoint I should use, shall I design a common endpoint so that it can take other updates later on on other places as well or just specific to this one?&lt;/li&gt;
&lt;li&gt;If I designed it to be more general, how could I prevent it from updating other things I don&amp;rsquo;t want it to?&lt;/li&gt;
&lt;li&gt;Authentication / CORs &amp;hellip;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Please notice that there are lots of other nuances in between that I omitted, I also deliberately uses &amp;ldquo;callback&amp;rdquo; event here because that&amp;rsquo;s a more familiar term for client side engineers. But my point is by looking at this new flow, you will realize there is a huge mind shifting difference: &lt;strong&gt;LiveView enables you to think closer to the data source&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With LiveView, sometimes you almost feels like you are coding everything in one machine without the internet. Because it eliminated the events listening, the network call, the endpoint handles it, the data contracts design between client and server, the serialization of the data passing back and forth. By &lt;strong&gt;saving all these mental bandwidth&lt;/strong&gt;, it enables you to think &lt;strong&gt;more closely to the data source&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s such a blessing to be not thinking all the data flows, endpoints in order to flip a switch. But there is a catch, not technically (though there indeed is, but not the major thing I want to focus on this article), but mentally about how you think about the your UI or UX.&lt;/p&gt;
&lt;p&gt;I found out comparing to traditional client side UI frameworks like React or Svelte, the main impact it has over my mind is how I approach the UI/UX. With the power of seamlessly communication between client and server side, a lot of times I would easily falls into eagerly manipulating the data bits, being more obsessed with the data, sometimes by sacrificing the user experiences.&lt;/p&gt;
&lt;p&gt;I think this is not an issue just applying to myself, people doing both design and coding were often impacted by this as well.&lt;/p&gt;
&lt;p&gt;I found myself ended up asking different kind of questions using a client side framework vs LiveView. Because client side JavaScript, obviously, enables you to think more closely the the human being, cause it&amp;rsquo;s naturally, physically closer to the user. While your elixir function is ultimately lived inside a different process in a distant warm data-center.&lt;/p&gt;
&lt;p&gt;When I tried to use Svelte (or any other client side framework, I will have another post on why I love Svelte so much) to replace some of the UI, I found out that &lt;strong&gt;it enables me to &amp;ldquo;sit&amp;rdquo; more closely with the user&lt;/strong&gt;, thus it focus me to think more from the actual user perspective on how they use the product, how we should present the data using what transition or animation so that it&amp;rsquo;s more &amp;ldquo;accessible&amp;rdquo; to them.&lt;/p&gt;
&lt;p&gt;Productivity is a double edge sword. Being able to do things extremely fast can enables us delivering features we can&amp;rsquo;t do yesterday, but sometimes it comes with an &lt;strong&gt;invisible cost or benefits to our brain&lt;/strong&gt;, to the ways we are thinking about a problem.&lt;/p&gt;
&lt;p&gt;I would admit that I&amp;rsquo;ve both overused client side frameworks and LiveView in the past. By overusing them, I would quickly found out the cons of the tools in a more tangible way.&lt;/p&gt;
&lt;p&gt;I sill love LiveView (and Elixir) and Svelte. Using these tools just makes me happy.&lt;/p&gt;
</description>
    </item>
    
    
  </channel>
</rss>
