<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Bigi Lui</title>
    <description>The latest articles on DEV Community by Bigi Lui (@bigi).</description>
    <link>https://dev.to/bigi</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F362690%2F3c1ef64d-f974-4204-87d6-f8bcdf6eaf07.png</url>
      <title>DEV Community: Bigi Lui</title>
      <link>https://dev.to/bigi</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bigi"/>
    <language>en</language>
    <item>
      <title>Karaoke Maker: From Music Video to KTV</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Wed, 11 Jun 2025 22:53:18 +0000</pubDate>
      <link>https://dev.to/bigi/karaoke-maker-from-music-video-to-ktv-52op</link>
      <guid>https://dev.to/bigi/karaoke-maker-from-music-video-to-ktv-52op</guid>
      <description>&lt;p&gt;A few years ago I first learned about &lt;a href="https://ultimatevocalremover.com/" rel="noopener noreferrer"&gt;Ultimate Vocal Remover&lt;/a&gt;, a desktop program created to work with several different ML models that split instrumentals and vocal tracks from a song. I learned about this in the context of learning about voice cloning back then. When I saw how well the vocal remover models worked, I immediately thought it would've been perfect for making karaoke videos.&lt;/p&gt;

&lt;p&gt;The problem with many karaoke videos (those found in karaoke places) is multiple folds: (1) poor instrumental quality that sometimes sound nothing like the original song (2) pitch is sometimes off (3) the videos are sometimes completely unrelated to the original song or music video.&lt;/p&gt;

&lt;p&gt;I finally got around to building something from this idea I had for a few years. This is now working in Python script format; perhaps one day I'll build a desktop app or web app with this tech.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Idea
&lt;/h2&gt;

&lt;p&gt;The primary goal is to take a standard video file (e.g., MP4) as input and produce a new video file with two key features:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Karaoke Audio Mix:&lt;/strong&gt; The audio is re-mixed into a stereo track where the left channel contains the instrumental-only version of the song, and the right channel contains the original, unprocessed audio. This allows for easy toggling between karaoke and original versions in a media player (common in karaoke places). I also added an option to use just the instrumental-only track, if that's what we want to output.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Synchronized Lyrics:&lt;/strong&gt; The lyrics of the song are transcribed and embedded into the video as karaoke-style subtitles, highlighting each word as it is sung.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Technology Stack
&lt;/h2&gt;

&lt;p&gt;The application is written in Python, orchestrating several key pieces of tech:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Audio/Video Processing:&lt;/strong&gt; &lt;code&gt;ffmpeg&lt;/code&gt; is used as the workhorse for all audio/video manipulation, including extraction, stream copying, and final re-assembly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vocal Separation:&lt;/strong&gt; &lt;code&gt;onnxruntime&lt;/code&gt; runs the &lt;code&gt;UVR_MDXNET_Main.onnx&lt;/code&gt; model, a high-quality vocal separation model from the Ultimate Vocal Remover project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speech-to-Text:&lt;/strong&gt; &lt;code&gt;faster-whisper&lt;/code&gt;, an optimized implementation of OpenAI's Whisper model, is used for fast and accurate transcription of the isolated vocals to generate word-level timestamps.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Application Workflow
&lt;/h2&gt;

&lt;p&gt;The conversion process is a four-step pipeline. The entire process is automated by a single Python script.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Step 1: Audio Extraction
&lt;/h3&gt;

&lt;p&gt;The process begins by using &lt;code&gt;ffmpeg&lt;/code&gt; to extract the full original audio track from the input video file. It is saved as an uncompressed WAV file. This original audio track is also preserved for use in the final video's right audio channel. (If using that option)&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Vocal Separation
&lt;/h3&gt;

&lt;p&gt;The extracted WAV file is fed into a vocal separation module. This module uses the pre-trained &lt;strong&gt;&lt;code&gt;UVR_MDXNET_Main.onnx&lt;/code&gt;&lt;/strong&gt; model. This is an MDX-Net (Music Demixing Network) model that excels at separating music into its component stems. We configure it to produce two outputs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Instrumental Track:&lt;/strong&gt; A WAV file containing only the music.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vocal Track:&lt;/strong&gt; A WAV file containing only the vocals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Step 3: Lyrics Generation
&lt;/h3&gt;

&lt;p&gt;The isolated vocal track is then passed to the transcription module. We use the &lt;strong&gt;&lt;code&gt;faster-whisper&lt;/code&gt;&lt;/strong&gt; library with the &lt;code&gt;small&lt;/code&gt; model for a good balance of speed and accuracy. The library is configured to perform transcription and generate word-level timestamps. These timestamps are then formatted into an &lt;strong&gt;Advanced SubStation Alpha (&lt;code&gt;.ass&lt;/code&gt;)&lt;/strong&gt; subtitle file. The &lt;code&gt;.ass&lt;/code&gt; format is specifically chosen because it supports karaoke-style effects, allowing us to specify the duration of each word for the "bouncing ball" highlighting effect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Final Video Assembly
&lt;/h3&gt;

&lt;p&gt;In the final step, &lt;code&gt;ffmpeg&lt;/code&gt; is called again to construct the output karaoke video. It combines four distinct sources:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Original Video Stream:&lt;/strong&gt; The video (without audio) is copied directly from the input file to prevent any loss of quality from re-encoding.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Instrumental Audio (Left Channel):&lt;/strong&gt; The instrumental track from Step 2 is mapped to the left channel of the new stereo audio track.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Original Audio (Right Channel):&lt;/strong&gt; The original, unprocessed audio from Step 1 is mapped to the right channel.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Karaoke Subtitles:&lt;/strong&gt; The &lt;code&gt;.ass&lt;/code&gt; file from Step 3 is burned directly onto the video frames.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result is a self-contained karaoke video file, ready for playback.&lt;/p&gt;

&lt;p&gt;Here is an example of what the program did:&lt;/p&gt;

&lt;p&gt;Original music video: &lt;a href="https://www.youtube.com/watch?v=Oextk-If8HQ" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=Oextk-If8HQ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Karaoke output: &lt;a href="https://www.youtube.com/watch?v=0dBf14_TLZc" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=0dBf14_TLZc&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Clean monolith code repo structure for a small JAM web app</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Sun, 24 Jan 2021 23:21:44 +0000</pubDate>
      <link>https://dev.to/bigi/clean-monolith-code-repo-structure-for-a-small-jam-web-app-1l61</link>
      <guid>https://dev.to/bigi/clean-monolith-code-repo-structure-for-a-small-jam-web-app-1l61</guid>
      <description>&lt;p&gt;About a year ago I set out to start a new web app project, that would combine the uses of some of my favorite technologies that I've picked up over the past few years.&lt;/p&gt;

&lt;p&gt;One of my main beefs with existing standards for modern web apps (typically in a server-side-rendered React stack) is how difficult the code repo's structure is to work with everyday. One of my specific goals in this project is to figure out a way to structure a web app, with both its backend and frontend, in a mono-repo (monolithic code repository), in a way that is easily understood and navigated.&lt;/p&gt;

&lt;p&gt;I should emphasize that I also specifically picked to go with a JAM stack for this project, which means that this is certainly entirely not applicable for a server-side-rendered codebase (e.g. Next.js with SSR, etc.). The frontend is a statically-generated site.&lt;/p&gt;

&lt;p&gt;The other emphasis I would make is that I'm going with a full Javascript codebase (so a frontend JS framework, with a node.js backend, instead of something like python or ruby). This allows me to share some code between backend and frontend, which is another goal of mine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Folder structure
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RzRL9Sin--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/iwigfk8mnyc5saeeeyr6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RzRL9Sin--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/iwigfk8mnyc5saeeeyr6.png" alt="My JAM stack app folder structure"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I figured the easiest way to understand the folder structure I want to describe is to visualize it as a tree from the code editor.&lt;/p&gt;

&lt;p&gt;Immediately you'll notice a few things: There's only a simple layout of a &lt;code&gt;backend&lt;/code&gt; folder, a &lt;code&gt;frontend&lt;/code&gt; folder, and a small handful of other root level files and folders. We'll do a quick run-down of the important elements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Naming
&lt;/h3&gt;

&lt;p&gt;You'll immediately notice that instead of going for more technically-focused terms like &lt;code&gt;client&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt;, I opted to go with a very English-centric vocabulary for the folders, i.e. just &lt;code&gt;frontend&lt;/code&gt; and &lt;code&gt;backend&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I wanted there to be no thinking required to immediately figure out how the codebase is laid out, no matter how many years I've been away from the project and not remember anything.&lt;/p&gt;

&lt;p&gt;Using terms like &lt;code&gt;client&lt;/code&gt; or &lt;code&gt;server&lt;/code&gt; always gives me a double-take. If I don't remember that this is a JAM stack app at all, &lt;code&gt;server&lt;/code&gt; makes me question whether it has anything to do with a server-side-rendered code, the server of the frontend site, or if it would be the API backend. (or even some other server-side scripts or configuration) Using &lt;code&gt;backend&lt;/code&gt; instead makes it clear that it is indeed the API backend, and not anything else.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;backend&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;As this is a JAM stack app, this is where the API server is. For my project, I chose to go with a &lt;a href="https://www.fastify.io/"&gt;Fastify&lt;/a&gt; server. The main thing to point out here is what is contained within the &lt;code&gt;backend&lt;/code&gt; folder. In short, imagine if you were building a backend-only project (Pick an &lt;a href="https://github.com/fastify/fastify-example-todo"&gt;example Fastify project&lt;/a&gt; if you'd like). Simply, the entire repo of the backend project would belong within here, except &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(Later we'll look at what the root level &lt;code&gt;package.json&lt;/code&gt; contains and see how it is shared with frontend.)&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;frontend&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;I'll just come out and say I'm not a fan of React. I chose Svelte for the project (and I believe I'll choose this for every personal web project going forward), but whether you choose Svelte, Vue or React, the same concept should apply here.&lt;/p&gt;

&lt;p&gt;Similar to backend, imagine you were building a frontend-only project. The entire repo of the frontend project would belong within here, again except for &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;lib&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is where backend and frontend shared modules are. In my case, I am building a simple card game. Some of the logic of the game belongs here. Later on, we'll take a look to see what a module that has different behavior on frontend and backend might look like here.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;package.json&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This probably what ties it all up. The important things here to look at are the &lt;code&gt;scripts&lt;/code&gt; section and the dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;check-format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prettier --list-different './lib/**/*.js' './test/**/*.js'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;prettier --write '*.js' './**/*.js'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node ./node_modules/eslint/bin/eslint './lib/**/*.js' './test/**/*.js'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tap ./test/**/*.test.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;backend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node backend/index.js | pino-pretty -m message --ignore level,pid,hostname,v&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;backend:raw&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node backend/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node backend/index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;frontend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node frontend/export-env.js &amp;amp;&amp;amp; rollup -c -w&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;frontend:serve&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sirv frontend/public&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;build&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node frontend/export-env.js &amp;amp;&amp;amp; rollup -c&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first four, &lt;code&gt;check-format&lt;/code&gt;, &lt;code&gt;format&lt;/code&gt;, &lt;code&gt;lint&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt; are mostly standard scripts for use with any js projects. They are applicable for both frontend and backend source files. My tools of choice are &lt;code&gt;prettier&lt;/code&gt;, &lt;code&gt;eslint&lt;/code&gt; and &lt;code&gt;tap&lt;/code&gt; which are pretty standard. I have a strong preference for &lt;code&gt;tap&lt;/code&gt; over any other testing frameworks like &lt;code&gt;mocha&lt;/code&gt; or &lt;code&gt;jest&lt;/code&gt; for the simplicity.&lt;/p&gt;

&lt;p&gt;The following scripts are more interesting. The &lt;code&gt;backend&lt;/code&gt; scripts make it clear which part of the stack they are starting. The default &lt;code&gt;backend&lt;/code&gt; starter script is what I use for development, and it pipes logs through &lt;code&gt;pino-pretty&lt;/code&gt; to make it much easier to read. The generically named &lt;code&gt;start&lt;/code&gt; script is to work with Heroku. I deploy my projects to Heroku so having the default server start script named &lt;code&gt;start&lt;/code&gt; makes it a breeze to set up with no additional config needed.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;frontend&lt;/code&gt; scripts are similar: the main &lt;code&gt;frontend&lt;/code&gt; script is what I use during local development (supports hot reload with &lt;code&gt;rollup&lt;/code&gt;). The &lt;code&gt;build&lt;/code&gt; step is for Netlify to run on deploy. The &lt;code&gt;export-env.js&lt;/code&gt; is a small script that runs in order to take environmental variables I define on Netlify and write them into a file so the frontend bundle can use them. Needless to say this shouldn't contain any secret tokens or anything; but we can use these for any client-side public tokens we use (e.g. Google Analytics, etc.).&lt;/p&gt;

&lt;h4&gt;
  
  
  Dependencies
&lt;/h4&gt;

&lt;p&gt;One notable thing here is that, every frontend-only package dependency in the project can simply be put in &lt;code&gt;devDependencies&lt;/code&gt;. Because the app is a JAM stack one, the frontend is entirely statically-generated. The generation process happens during build-time, hence only &lt;code&gt;devDependencies&lt;/code&gt; is needed.&lt;/p&gt;

&lt;p&gt;For shared or backend-only dependencies, we would include the package in &lt;code&gt;dependencies&lt;/code&gt;. This actually helps keep the list of &lt;code&gt;dependencies&lt;/code&gt; very small for the backend app.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;rollup.config.js&lt;/code&gt; changes
&lt;/h3&gt;

&lt;p&gt;A default rollup config file that comes with a sample Svelte/Sapper project is typically good to start, only having to update the path to the frontend app itself. In my case, the relevant lines in the rollup config look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;frontend/src/main.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;sourcemap&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;iife&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;frontend/public/build/bundle.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nx"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;svelte&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="c1"&gt;// enable run-time checks when not in production&lt;/span&gt;
      &lt;span class="na"&gt;dev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;production&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="c1"&gt;// we'll extract any component CSS out into&lt;/span&gt;
      &lt;span class="c1"&gt;// a separate file - better for performance&lt;/span&gt;
      &lt;span class="na"&gt;css&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;css&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;css&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;frontend/public/build/bundle.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;export-env.js&lt;/code&gt; for frontend
&lt;/h3&gt;

&lt;p&gt;As mentioned before, this file allows us to take Netlify-configured environmental variables and put them into the bundle. For safety reasons, we specify exactly which env vars we want to take (so we don't accidentally add secrets to the Netlify configuration and expose them to clients):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * This script is for Netlify builds.
 * Netlify allows you to set up env vars, but they only exist at build time.
 * We run this as part of the build to generate a json file, which will then get
 * loaded for use in frontend.
 */&lt;/span&gt;

&lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// for local builds with .env file&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./frontend-env.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`'use strict';\n\nmodule.exports = &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="c1"&gt;// The following list of env vars should match what's set on Netlify&lt;/span&gt;
    &lt;span class="na"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;WEBSOCKET_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;WEBSOCKET_URL&lt;/span&gt;
  &lt;span class="p"&gt;})}&lt;/span&gt;&lt;span class="s2"&gt;;\n`&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  No &lt;code&gt;packages&lt;/code&gt; folder, no separate npm packages, no lerna
&lt;/h3&gt;

&lt;p&gt;This is really my big thing here. When I worked with a mono-repo web app at a job in the past, I &lt;em&gt;hated&lt;/em&gt; the multiple packages setup with lerna. The &lt;code&gt;lerna bootstrap&lt;/code&gt; process always takes way too long, and it was often error-prone, not knowing when you have to bootstrap again and such.&lt;/p&gt;

&lt;p&gt;(And this is a small pet peeve of mine, but I hated how the root level of a frontend app using lerna would have a &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;packages&lt;/code&gt; directory, which really messes with my tab-auto-complete on the command line.)&lt;/p&gt;

&lt;p&gt;Here, &lt;em&gt;all&lt;/em&gt; of my frontend code (Svelte components, or Vue/React components if you choose) is in &lt;code&gt;frontend&lt;/code&gt;. Because I use Svelte and Rollup, I automatically get the benefit of tree-shakes, so even if my project grows large and I start having unused packages they won't pollute the bundle.&lt;/p&gt;

&lt;p&gt;Because I'm not doing server-side-rendering, there is generally little "isomorphic" code. The only shared code I have, is explicitly and intentionally placed into the &lt;code&gt;lib&lt;/code&gt; folder at root so I know they are shared; and there's no mistake. These are typically functional modules with logic only.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example shared module with different behavior on frontend and backend: &lt;code&gt;logger.js&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;One of the modules in my &lt;code&gt;lib&lt;/code&gt; folder is the logger module &lt;code&gt;logger.js&lt;/code&gt;. The reason I don't just simply have separate logger modules in frontend and backend is that I want to be able to use the logger in other &lt;code&gt;lib&lt;/code&gt; shared code themselves as well. My &lt;code&gt;logger.js&lt;/code&gt; module code looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use strict&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ENV&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./constants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// server: use pino&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pino&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pino&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdTimeFunctions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isoTime&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// client: logging only if dev&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;fatal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fatal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;warn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;warn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;debug&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;trace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;logFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are several things going on here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I use &lt;code&gt;typeof window === 'undefined'&lt;/code&gt; to detect for the global window object to determine if this is backend or frontend.&lt;/li&gt;
&lt;li&gt;On backend, I use the &lt;code&gt;pino&lt;/code&gt; logger.&lt;/li&gt;
&lt;li&gt;On frontend, only in a dev build, it uses &lt;code&gt;console.log&lt;/code&gt;. Otherwise, it's a no-op.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;I'm not expecting this blog post to start a big movement away from the current startup best practice with file structures in mono-repo codebases. I'm merely introducing how I'm doing it, and perhaps you'd find it useful for your projects, find flaws and make improvements to it.&lt;/p&gt;

&lt;p&gt;If there's any interests, I'd be happy to put together a sample project you can clone and run immediately with this structure, so you can play around with it more easily.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My current new Windows PC setup essentials in 2020</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Mon, 16 Nov 2020 07:37:44 +0000</pubDate>
      <link>https://dev.to/bigi/my-current-new-windows-pc-setup-essentials-in-2020-4acj</link>
      <guid>https://dev.to/bigi/my-current-new-windows-pc-setup-essentials-in-2020-4acj</guid>
      <description>&lt;p&gt;I've bought and set up a lot of new desktop/laptop PCs (usually Windows, in some cases Mac or Linux) over the past two decades; and the set of essential software I installed on a new computer has also evolved over the years.&lt;/p&gt;

&lt;p&gt;Most recently when I've set up a new Windows laptop for the family, I've found that the set of software I now install for a new Windows computer is now relatively meager and each are now much higher quality than the generations before.&lt;/p&gt;

&lt;p&gt;This has become a sort of compilation of "best practices" setup for myself / my family, so I figured it's nice to document it.&lt;/p&gt;

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

&lt;p&gt;This has really replaced a lot of things over the years; most notably in terms of security. I make sure to open up Windows Update (multiple times, and multiple restarts throughout the process) to be certain that I am fully up-to-date.&lt;/p&gt;

&lt;h3&gt;
  
  
  Security
&lt;/h3&gt;

&lt;p&gt;In the past I've had various anti-virus (e.g. &lt;a href="https://en.wikipedia.org/wiki/FRISK_Software_International#F-Prot_Antivirus"&gt;F-Prot&lt;/a&gt;, &lt;a href="https://www.avg.com/"&gt;AVG&lt;/a&gt;, &lt;a href="https://www.avast.com/"&gt;Avast&lt;/a&gt;, etc.), firewall (e.g. &lt;a href="https://www.zonealarm.com/"&gt;ZoneAlarm&lt;/a&gt;, PrivateFirewall, etc.), anti-spyware (e.g. Spybot Search &amp;amp; Destroy), etc. but it seems that Windows Defender, Windows Firewall and Malicious Software Removal Tool has evolved enough over the years that they have been a good enough replacement for everything, along with up-to-date windows security updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Language
&lt;/h3&gt;

&lt;p&gt;Language tools in Windows is another category where Windows has completely replaced the need for 3rd party. As a Chinese person, who types some Chinese text from time to time, I've been through software like &lt;a href="https://www.its.hku.hk/news/archive/ccnews86/RichWin.html"&gt;RichWin&lt;/a&gt;, &lt;a href="https://www.linguistsoftware.com/cjkpartner.htm"&gt;TwinBridge&lt;/a&gt;, &lt;a href="https://www.njstar.com/"&gt;NJStar&lt;/a&gt;, etc. over the years, but nowadays I simply make do with the Pinyin input that comes with Windows as well as &lt;a href="https://www.cantoneseinput.com/"&gt;CantoneseInput.com&lt;/a&gt;, a web-based input editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Some Windows settings
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Cursor size
&lt;/h4&gt;

&lt;p&gt;It's probably something that comes with old age, but I find that on almost every computer I've set up, the default cursor size (considering the screen resolution and screen sizes nowadays) is too small to see. I bump this up a bit as one of the first things to set.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trackpad scroll direction
&lt;/h4&gt;

&lt;p&gt;Maybe I'm old school and not of the "mobile generation", but I find that I can only use the mobile scroll direction on mobile. It just doesn't make sense on a laptop; but unfortunately both macOS and Windows decided nowadays that the mobile scroll direction (opposite scroll) should be the default. I have to flip this every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blue Light Filter - Flux
&lt;/h2&gt;

&lt;p&gt;It's probably something that comes with old age again, but I simply can't use a regular-colored screen in the evening hours anymore. It gives me a migraine pretty much within a minute of use. I &lt;em&gt;have&lt;/em&gt; to get &lt;a href="https://justgetflux.com/"&gt;Flux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There are a number of options now for blue light filters, and mobile OS like Android even comes with this feature by default now. On Windows I still use Flux, which I've used for many years. It has a lot of niceties around scheduling and easy customization of color temperature and such.&lt;/p&gt;

&lt;h2&gt;
  
  
  Browser - Edge, Chrome, Firefox
&lt;/h2&gt;

&lt;p&gt;I'll be honest, I think we're well past the days of the Microsoft browser being the butt of the joke (as in, Internet Explorer). Edge is a very legitimate browser in its own right. Both as a dev, and for simply having more options as a user when needed (believe or not, there are still sites out there that don't work on certain browsers), I like having all 3 installed. Note that this also means manually updating Edge to the latest because the one that comes with Windows 10 as of now (Nov 2020) still isn't the latest.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adblock plugin - ublock origin
&lt;/h3&gt;

&lt;p&gt;Lucky for us, in 2020, &lt;a href="https://en.wikipedia.org/wiki/UBlock_Origin"&gt;ublock origin&lt;/a&gt; is available on all 3 browsers. This is the only adblock or privacy blocking plugin I'd use. The more popular AdBlock Plus is way too bloated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Office - SoftMaker FreeOffice
&lt;/h2&gt;

&lt;p&gt;This was a relatively recent discovery for me. I had stopped using Microsoft Office many years ago mainly to be cost-conscious; and for years it has been sufficient to use either Apache OpenOffice or Google Docs. More recently I found out about &lt;a href="https://www.freeoffice.com/"&gt;FreeOffice&lt;/a&gt; and in my brief experience so far it seems superior to &lt;a href="https://www.openoffice.org/"&gt;OpenOffice&lt;/a&gt;, which is slow and often looks dated. Admittedly, I have not tried &lt;a href="https://www.libreoffice.org/"&gt;LibreOffice&lt;/a&gt;, but currently I'm happy with FreeOffice.&lt;/p&gt;

&lt;h2&gt;
  
  
  Graphics - Krita
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://krita.org/"&gt;Krita&lt;/a&gt; was another relatively recent discovery for me. For more than 20 years I had used &lt;a href="https://winworldpc.com/product/paint-shop-pro/6x"&gt;Jasc Paint Shop Pro 6&lt;/a&gt; for basic graphics editing needs (mostly things like resizing/scaling, canvas sizing, or cropping images). In the last few years it has finally begun to age and I've run into a lot of issues with more modern image formats and such (even transparent PNGs aren't handled well).&lt;/p&gt;

&lt;p&gt;For this reason, the last few years I found myself doing a lot of work with the web-based &lt;a href="https://pixlr.com/editor/"&gt;Pixlr Editor&lt;/a&gt;, and more recently &lt;a href="https://www.photopea.com/"&gt;Photopea&lt;/a&gt;. However, web-based editors always felt lacking and slow. I've tried using &lt;a href="https://www.gimp.org/"&gt;Gimp&lt;/a&gt; (another free alternative) as well but have never been satisfied.&lt;/p&gt;

&lt;p&gt;Having tried Krita briefly, I can say it appears to satisfy all my basic graphics needs quite well and I can see myself being a long term user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Communications - Zoom
&lt;/h2&gt;

&lt;p&gt;In these COVID times with social distancing, a lot of times hanging out with friends comes down to video conferencing. In most cases, &lt;a href="https://zoom.us/"&gt;Zoom&lt;/a&gt; is everyone's choice, so here it is. Interesting to note that over the years, the choice of communication software has ranged from &lt;a href="https://en.wikipedia.org/wiki/ICQ"&gt;ICQ&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/AIM_(software)"&gt;AIM&lt;/a&gt;, &lt;a href="https://en.wikipedia.org/wiki/Windows_Live_Messenger"&gt;MSN messenger&lt;/a&gt;, Skype to combo ones like &lt;a href="https://trillian.im/"&gt;Trillian&lt;/a&gt; or &lt;a href="https://adium.im/"&gt;Adium&lt;/a&gt;. Nowadays text-based messengers have mostly moved to options that are on mobile like WhatsApp and Facebook messenger. Video calls, though, are still common on the computer.&lt;/p&gt;

&lt;p&gt;I hope this list helped you discover some useful free software that you previously did not know about as well!&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Fav HN Comment - "Less Code Fallacy"</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Wed, 14 Oct 2020 06:30:35 +0000</pubDate>
      <link>https://dev.to/bigi/fav-hn-comment-less-code-fallacy-4kbk</link>
      <guid>https://dev.to/bigi/fav-hn-comment-less-code-fallacy-4kbk</guid>
      <description>&lt;p&gt;&lt;em&gt;Hacker News is certainly a good source of aggregated news (tech news, but also general news) and information. But more than that, I find the comments section to be the best. Every now and then I come across absolute gems. Brilliant people are everywhere, but not every one of them writes blogs! For the most part, these have been buried in my "upvoted comments" section, and not many people ever got a chance to read these comments. This is a blog series I hope to highlight some of my favorite HN comments in the past.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;On &lt;a href="https://news.ycombinator.com/item?id=23297463"&gt;Software will eat software in a remote-first world&lt;/a&gt; (&lt;a href="https://themargins.substack.com/p/software-will-eat-software-in-a-remote"&gt;Original article&lt;/a&gt;), comment written by &lt;a href="https://news.ycombinator.com/item?id=23298080"&gt;0xB31B1B&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I think your fallacy is in the "less code" assumption when you say "The result is less code". I'd argue that empirically we've seen this to be false. The result isn't less code, at least in a global sense, its more productivity, more features, more customization, and more specificity at a cost of less code/feature. Software has really interesting economics where as the cost/feature decreases by a factor, say 1x, then the set of features that can be profitably worked on expands by like 10x, so paradoxically, as cost/feature decreases, it makes sense to hire more engineers and expand R&amp;amp;D budgets.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is such counterintuitive insight that I admit I had not thought of until I read the comment, yet it's so true.&lt;/p&gt;

&lt;p&gt;The topic of the thread was about no-code/low-code tools (e.g. site builders, etc.); but I think it applies just as well to basically any level of abstractions and frameworks.&lt;/p&gt;

&lt;p&gt;What used to take a lot longer, newer frameworks and technologies allow you to build software much faster. Technologies across the spectrum including AWS (in general), NoSQL data stores (compared to SQL DBs), frontend frameworks, enable you to build entire web apps as a solo dev -- the exact same apps that used to take entire teams to build.&lt;/p&gt;

&lt;p&gt;I worked on text-based Facebook games back in 2008. We ran LAMP stack with an in-house built MVP framework in PHP, with basically no frontend framework (raw native JS only), running on bare metal servers that our infrastructure team had to assemble in our data centers.&lt;/p&gt;

&lt;p&gt;The same type of games could be built in 2020 with modern technologies (React/Svelte for frontend, Redis for caching and various data structures stores for performance, MongoDB/Couchbase for persistent storage) and infrastructure (AWS with EC2/ECS/EKS for auto-scaling, RDS for a managed data store, Elasticache for Redis) and scale to the similar volume of traffic (several millions of daily users) with a much smaller team, with even more stable infrastructure than what we used to have.&lt;/p&gt;

&lt;p&gt;What happens then? As a company, in this specific example, it then becomes profitable to build a lot more features, or simply build more games. Because you can now build the same product you did 10 years ago with a 10x smaller team (as an example), that frees up 90% of your team cost -- which in turn means (all else being equal) you could theoretically invest the 90% of the team you freed up to build 9 more games/apps, for 10x the profit. Of course, this is just to demonstrate a point -- in the real world, things aren't quite as simple and straightforward; but the same concept is certainly applicable.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Big2 - Breaking Down a Project Into Phases</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Sun, 27 Sep 2020 05:10:59 +0000</pubDate>
      <link>https://dev.to/bigi/big2-breaking-down-a-project-into-phases-n75</link>
      <guid>https://dev.to/bigi/big2-breaking-down-a-project-into-phases-n75</guid>
      <description>&lt;p&gt;Finding time to work on a hobby project is among one of the hard things about doing one. What makes it harder is the context you lose between the time you work on it. And when it comes to a reasonably sized project -- anything bigger than something you could complete over a weekend, with some degree of complexity that requires some planning and system design -- losing the context that was once fresh in your head could also mean losing interests/passion and abandoning ship.&lt;/p&gt;

&lt;p&gt;The Big2 game project was like that for me. &lt;a href="https://bigi.dev/blog/2020-04-22-big-2-card-game-svelte-jam-stack/"&gt;I started it a few months ago&lt;/a&gt;, and life got in the way and I paused on it.&lt;/p&gt;

&lt;p&gt;When I started it, I had big plans of online play that works both synchronously &amp;amp; asynchronously, as well as various UI features and ideas. I started coding it all in one big sprint. I did take the time to organize code files sensibly, with clean backend/frontend separation (&lt;a href="https://bigi.dev/blog/2020-04-22-big-2-card-game-svelte-jam-stack/"&gt;more on the stack and system design here&lt;/a&gt;) while taking advantage of Svelte's component structures; and follow best practices with the actual code.&lt;/p&gt;

&lt;p&gt;Nevertheless, having taken a hiatus for a few months, coming back to it was hard. A lot of the system design I had in mind of client-server communication (how the server handled client connections and distribute data to each) was lost in my head and what code I had written was hard to follow.&lt;/p&gt;

&lt;p&gt;I decided to take a step back from all of that network play code mixed in with game client/UI code, and broke down the project to different phases that I could work on separately, with each phase being small enough for me to complete over a few weeknights.&lt;/p&gt;

&lt;p&gt;It came down to the following 3 main areas:&lt;/p&gt;

&lt;h2&gt;
  
  
  Game Logic
&lt;/h2&gt;

&lt;p&gt;This further breaks down to two sub-points:&lt;/p&gt;

&lt;h3&gt;
  
  
  Pure functions that handle the logic of the Big2 game rules
&lt;/h3&gt;

&lt;p&gt;Before I even split the project up to the 3 main areas, this was actually the very first part of the codebase I worked on. It made sense at the time too; I foresaw having to have game logic like this on both client side and server side (client side could quickly determine if a move is valid before a server request, and server to validate and execute a move). The actual final design turned out a little different, but this shared lib was useful nevertheless, because having a local game mode.&lt;/p&gt;

&lt;p&gt;These are pure functions (in functional programming terms) where you provide an input value, and it runs through some logic and gives an output. No database, no state, no request context, no client.&lt;/p&gt;

&lt;p&gt;For example, here's what a function might look like, which validates a move when everybody passed and it's your turn to play any card:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * next is a card or array of cards the player wants to play
 */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// Singles&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// Pairs&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// not pair&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// Triples&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// not triple&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="c1"&gt;// Five Card Set&lt;/span&gt;
    &lt;span class="c1"&gt;//&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fiveCardType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFiveCardType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cards&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;fiveCardType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// invalid set&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// invalid move&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of the game logic of a game of Big2 is written as a set of a dozen or so pure functions like the one above. This includes everything from checking whether a set of five cards is a straight / flush / full-house / etc., checking if the cards you're playing is greater in power than what was played by the previous player, and so on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pure functions that handle players starting games and taking turns
&lt;/h3&gt;

&lt;p&gt;Once the above set of pure functions for game logic is done, this is really a small subset that takes care of the flow of the game.&lt;/p&gt;

&lt;p&gt;This set of pure functions would take in the entire Game State object, and determine whose turn is next, what happens when they take a turn or do a pass, update the Game State for everything that needs to happen, and so on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local Game Mode
&lt;/h2&gt;

&lt;p&gt;This phase basically allowed me to flesh out the UI and frontend.&lt;/p&gt;

&lt;p&gt;Once all of the above pure functions that handle game logic and game flow are done, working on the local game mode is completely Svelte frontend code.&lt;/p&gt;

&lt;p&gt;The concise nature of Svelte frontend components code as well as the structures of how a Svelte web app is laid out make this just about as easy and fun as frontend could be to work on. As someone who used to advise against React and for simply using jQuery frontends for hobby projects, I have to say Svelte takes the cake in ease of development. Svelte offers the best of both worlds -- ease of getting started and concise code like a jQuery ad-hoc frontend would give, while providing the code structure and component-based design like React does that a big frontend project needs, in order to not become a spaghetti mess that a jQuery frontend codebase often becomes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Online Game Mode
&lt;/h2&gt;

&lt;p&gt;Leaving this part to the end allowed me focus on the networking aspect of it, as well as other online features like setting your name and avatar.&lt;/p&gt;

&lt;p&gt;At this point, all game logic is done, and I have a simplified game frontend mostly working in a local game mode. Sure, the online mode will have some extra features, but at this point the UI is now in a state where I can make iterative updates, instead of starting from scratch.&lt;/p&gt;

&lt;p&gt;This let me dive back into the websockets network game code I had when I first started the project, made sense of what was there, made necessary revamps to better work with the now more independent components of the rest of the codebase.&lt;/p&gt;

&lt;p&gt;At the end of the day, getting the online game mode working became a lot easier and shorter than I would've anticipated. I remember at one point coming to a mental revelation of something like, "Wait, this is it. I think I'm actually only a few steps away from a fully working online game. I basically just wire up an API request of what happens when a player clicks a button to take a turn, and the rest of the websockets code that takes a request, execute it, and broadcast the updated Game State to all clients, would just handle the rest." Indeed, once I did that, the client code would receive an updated Game State, render the entire game stage (game board) correctly; and then automatically all players in the game have a fully working game.&lt;/p&gt;

&lt;p&gt;What a satisfying feeling! I hadn't even expected the online game to be fully working that soon, but there it is.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Svelte Reactivity and Stores</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Thu, 27 Aug 2020 05:42:25 +0000</pubDate>
      <link>https://dev.to/bigi/svelte-reactivity-and-stores-340j</link>
      <guid>https://dev.to/bigi/svelte-reactivity-and-stores-340j</guid>
      <description>&lt;p&gt;I don't code in Svelte for my day job, so I only get to work with it on my hobby projects. Like most people, I only occasionally have time to work on them. I will have an ongoing project that I just randomly take a hiatus from because life, and then a month/a few months would pass by (or maybe even a year later), I get back to work on it.&lt;/p&gt;

&lt;p&gt;Svelte is an intuitive framework and once you learn the basics, it's easy to navigate a Svelte codebase and get things done. Occasionally there are quirks that catch me off-guard, though.&lt;/p&gt;

&lt;p&gt;One of the things that have caught me multiple times now, after taking a break and coming back to a project, is how Svelte handles reactivity with its Stores.&lt;/p&gt;

&lt;p&gt;Let's say you have some Writable Stores like this, set up in a &lt;code&gt;stores.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svelte/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;status_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Howdy&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;game_state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;({});&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cards&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;writable&lt;/span&gt;&lt;span class="p"&gt;([]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You use these stores normally in your other Svelte components. So you have things like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;status_message&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;status_message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$status_message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All good so far. Let's say though in some cases you put an object or an array into a Writable Store, like the example above with &lt;code&gt;game_state&lt;/code&gt; and &lt;code&gt;cards&lt;/code&gt;. Because usually you intend to update only some subset of the fields within the object, you may write your code like this in a component that updates it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;game_state&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;$game_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_turn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="nx"&gt;turn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$game_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_turn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you'll find that &lt;code&gt;game_state&lt;/code&gt; does not seem to be updated, even though you're using it as an Auto-subscribed Store by virtue of using &lt;code&gt;$&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  You have to use &lt;code&gt;.set()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The gotcha here is that reactivity is triggered by the &lt;code&gt;.set()&lt;/code&gt; call, not simply the data in the Writable Store being updated. So the way to do something like the example above would be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;game_state&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./stores&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;$game_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_turn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;game_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;$game_state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Alternatively, use .update() instead of .set()&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Current&lt;/span&gt; &lt;span class="nx"&gt;turn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$game_state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current_turn&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you think about the internal workings of Svelte, it makes sense; the specific functions of &lt;code&gt;.set()&lt;/code&gt; or &lt;code&gt;.update()&lt;/code&gt; on a Writable Store is what kicks off re-evaluation and reactivity based on the value of that store.&lt;/p&gt;

&lt;p&gt;When I'm just coding though, I tend to think of a Store as simply a variable that can be accessed globally from anywhere in the frontend app and that changes to it immediately reflect everywhere else. Because of that, I tend to forget what I really should do to kick off a reactive update, especially when what's in the Store is an object or array.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Svelte/Sapper Issue with Switching Components</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Mon, 24 Aug 2020 05:48:34 +0000</pubDate>
      <link>https://dev.to/bigi/svelte-sapper-issue-with-switching-components-3e4l</link>
      <guid>https://dev.to/bigi/svelte-sapper-issue-with-switching-components-3e4l</guid>
      <description>&lt;p&gt;I ran into what might be my first Svelte bug when working on the &lt;a href="https://dev.to/blog/2020-04-22-big-2-card-game-svelte-jam-stack"&gt;Big2 card game project&lt;/a&gt;. Very roughly, I have a page setup that looks like this (&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; sections omitted, and unrelated component properties omitted):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;$state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;game&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameStageLocal&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;$state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DealAnimation&lt;/span&gt; &lt;span class="nx"&gt;onDone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dealingDone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Initializing&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;&amp;lt;DealAnimation&amp;gt;&lt;/code&gt; component plays some game start animation and when it's done, it calls the &lt;code&gt;onDone&lt;/code&gt; handler passed into it. What it will do is to update &lt;code&gt;state&lt;/code&gt; (a Writable Store) to &lt;code&gt;game&lt;/code&gt; so the interactive game UI will start.&lt;/p&gt;

&lt;p&gt;As you can deduce from the code above, &lt;code&gt;$state&lt;/code&gt; is an auto-subscribed store so when its value changes, it would change the DOM on the page such that &lt;code&gt;&amp;lt;GameStageLocal&amp;gt;&lt;/code&gt; is now what's on the page, and &lt;code&gt;&amp;lt;DealAnimation&amp;gt;&lt;/code&gt; would be removed from the DOM.&lt;/p&gt;

&lt;p&gt;When testing it, the Firefox console gave the following error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: t.parentNode is null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was a bit baffling since I couldn't really find an error in my Svelte code and there wasn't any error in the Svelte dev build output. Upon searching online, I came across &lt;a href="https://stackoverflow.com/questions/58768701/sapper-how-do-i-fix-parentnode-is-null-when-navigating-away-from-a-page-with"&gt;this StackOverflow post&lt;/a&gt; which has exactly my issue. Although the answer mentions that:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ran into this error recently. In my case it was due to another library changing the DOM. (In my case fontawesome)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I was not using another library to change the DOM; only Svelte. Either way, though, the described fix in the answer worked for me, which was to wrap my components inside those conditions in another node, like below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;$state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;game&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;GameStageLocal&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nx"&gt;$state&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;DealAnimation&lt;/span&gt; &lt;span class="nx"&gt;onDone&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dealingDone&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;{:&lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Initializing&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sr"&gt;/if&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this worked fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parting Thoughts
&lt;/h2&gt;

&lt;p&gt;I think this does go to show the relative immaturity of Svelte in general, compared to React or Vue. I remain a fan of Svelte, but nevertheless it's good to keep in mind that when you use a small framework, you're bound to run into actual bugs in the framework.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>svelte</category>
    </item>
    <item>
      <title>Miner - Blog Framework Based on Sapper/Svelte</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Sat, 01 Aug 2020 22:12:16 +0000</pubDate>
      <link>https://dev.to/bigi/miner-blog-framework-based-on-sapper-svelte-2cjc</link>
      <guid>https://dev.to/bigi/miner-blog-framework-based-on-sapper-svelte-2cjc</guid>
      <description>&lt;p&gt;I open sourced the framework that powers my blog, and called it &lt;a href="https://github.com/bigicoin/miner"&gt;Miner&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;The README of the repo describes pretty well what it is and the story behind making it. It's based on Sapper, the web app framework based on the Svelte UI framework.&lt;/p&gt;

&lt;p&gt;Sapper supports regular SPA mode (Single Page Application), as well as server-side-rendering options, or even all the way to static site generation. The static site generator mode is what I use out of it.&lt;/p&gt;

&lt;p&gt;Now, Sapper is a great web app framework, but it's not specific for creating a blog. There are many basic features one would want for a blog, such as managing each post in Markdown files, defining meta-data for each post (e.g. as YAML Frontmatter), OpenGraph social tags for social network sharing optimizations, sharing icons, comments widget, and more.&lt;/p&gt;

&lt;p&gt;The Miner framework provides all of this out of the box. Quite literally, you can grab the repo, start writing Markdown files in the &lt;code&gt;posts&lt;/code&gt; directory and you'll have a blog running.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Name
&lt;/h2&gt;

&lt;p&gt;I'm not great at naming and I thought for a while what to name it. I ended up with Miner -- admittedly in this day and age, it's going to make folks think of crypto miners.&lt;/p&gt;

&lt;p&gt;In reality, it's an extension of the name Sapper. &lt;a href="https://en.wikipedia.org/wiki/Sapper#Miner"&gt;Miners&lt;/a&gt; are a specific type of sappers in war that attack forts. I thought it's appropriate since Miner is a framework for building sites of a specific narrow category (a blog).&lt;/p&gt;

&lt;h2&gt;
  
  
  Doing my part to push Svelte
&lt;/h2&gt;

&lt;p&gt;If you've been reading my blog, you know I'm a fan of Svelte. As a modern frontend framework, I think it's the one that makes the most sense among Angular, React, Vue, and now Svelte. Admittedly, out of these, I've only had extensive experience with React and Svelte; and only a cursory exploration with Angular and Vue in the past. From my experience though, a web app built on Svelte has the most concise code. Components have the cleanest code among different frameworks.&lt;/p&gt;

&lt;p&gt;Svelte is still relatively small, but if you look at the community, there's already a lot of things built upon it. Case in point, searching on npm for a Svelte component for a specific thing usually yields some results. The other day, I was looking for a &lt;a href="https://www.npmjs.com/package/svelte-color-picker"&gt;color picker&lt;/a&gt; and found one. If you're looking for a &lt;a href="https://www.npmjs.com/package/svelte-calendar"&gt;calendar component&lt;/a&gt;, there's one.&lt;/p&gt;

&lt;p&gt;If you've &lt;em&gt;really&lt;/em&gt; been reading my blog, you probably also realize it's not a secret that I dislike React. Right now, the tech industry as a whole basically develops frontend in React. I'd like to do my part to push for other frameworks to be viable, employable skillsets. Or, if you'd like to hear ir phrased more negatively, I'd like to do my part to dethrone React's monopoly status as the frontend framework of choice in the tech industry/among startups. Getting just one more developer comfortable and in favor of Svelte is another step toward that goal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Other Svelte Blogging Frameworks
&lt;/h2&gt;

&lt;p&gt;Because Sapper stops short of providing features of a blog, many people who've built a blog with Svelte &lt;a href="https://www.creativebloq.com/how-to/svelte-and-sapper"&gt;wrote&lt;/a&gt; &lt;a href="https://www.mahmoudashraf.dev/blog/build-a-blog-with-svelte-and-markdown/"&gt;tutorials&lt;/a&gt; to &lt;a href="https://medium.com/@mahmoudss1997/building-a-blog-with-svelte-sapper-and-markdown-29fd8c3506cb"&gt;build&lt;/a&gt; a &lt;a href="https://dev.to/joshnuss/create-a-blog-with-markdown-sapper-50ad"&gt;blog&lt;/a&gt; with Svelte/Sapper, or in some cases even provided a &lt;a href="https://github.com/joshnuss/sample-blog"&gt;sample repo&lt;/a&gt;, which is quite similar to Miner actually.&lt;/p&gt;

&lt;p&gt;However, I believe that a batteries-included framework can contribute a lot to adoption of a technology. Learning of any new tech takes ramp up time and effort. As individual elements, Svelte and Sapper may be easy to learn; and then once you've learned them, building basic features like those for a blog may be easy tasks; and finally wrapping your head around static site generation and a Netlify deploy may be simple once you've taken the time to read about it.&lt;/p&gt;

&lt;p&gt;But add all those together, now you've got a pretty high barrier of entry, if all you're trying to do is to start a blog. You could start one on Wordpress, or another static site blog generator that's more popular like &lt;a href="https://gohugo.io/"&gt;Hugo&lt;/a&gt; (written in go) or &lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; (written in ruby)... or you could take your time and learn all of above about Svelte, and then pick Sapper. I think most people would avoid this last route, given the options.&lt;/p&gt;

&lt;p&gt;However, what if you look at Jekyll's "Get up and running &lt;em&gt;in seconds&lt;/em&gt;." tagline, see the 4 commands it takes to start a blog:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gem install bundler jekyll
jekyll new my-awesome-site
cd my-awesome-site
bundle exec jekyll serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And compare it to Miner, which also involves just 4 commands to start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx degit "bigicoin/miner#master" my-blog
cd my-blog
npm install
npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you start to have competition. Now you're talking! That, at the end of the day, is what I'm trying to achieve.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>svelte</category>
    </item>
    <item>
      <title>On being a tech lead</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Fri, 10 Jul 2020 21:28:48 +0000</pubDate>
      <link>https://dev.to/bigi/on-being-a-tech-lead-1mp5</link>
      <guid>https://dev.to/bigi/on-being-a-tech-lead-1mp5</guid>
      <description>&lt;p&gt;A friend and coworker of mine from Zynga, &lt;a href="https://medium.com/@ellen.l.wong"&gt;Ellen&lt;/a&gt;, maintains a Slack group of tech leaders from various companies that I'm a part of. Recently, Ellen shared the following article on engineering leadership with the group: &lt;a href="https://charity.wtf/2017/05/11/the-engineer-manager-pendulum/amp/"&gt;The Engineer/Manager Pendulum&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I found it really insightful and echoing a lot of thoughts I've had, but hadn't been able to articulate. Here is a couple of excerpts from it that really rings true to me, and some points that I'd like to add to them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What one can learn from the pendulum
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;The best frontline eng managers in the world are the ones that are never more than 2-3 years removed from hands-on work, full time down in the trenches. The best individual contributors are the ones who have done time in management.&lt;/p&gt;

&lt;p&gt;And the best technical leaders in the world are often the ones who do both. Back and forth.  Like a pendulum.&lt;/p&gt;

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

&lt;p&gt;Conversely: the best tech leads in the world are always the ones who done time in management. This is not because they’re always the best programmers or debuggers; it’s because they know how to get shit done, which means they know how to communicate and manage other people.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;Up until the last few years, I had been stubbornly avoiding getting into a people management role. My nearly-6-year tenure in Zynga was spent squarely on the individual contributor / architect track. I took something that had some truth to it -- that "I'm not a people manager type" -- and mentally went all-in on it and did not even consider the possibility of going into management. (Zynga, like most large tech companies, had a management track and an IC track, so this was not an issue. As an early employee and important IC, I leveled up quickly from Software Engineer through Principal to eventually Architect.)&lt;/p&gt;

&lt;h4&gt;
  
  
  Stumbling upon a hybrid team lead / manager role
&lt;/h4&gt;

&lt;p&gt;After shutting down my startup CoinTent (where I spent 3 years hacking up products and running dev ops, doing nothing about team management or tech leadership), I was made an offer to join Massdrop as a Principal Software Engineer, first as an IC. Soon after I joined, a team lead role had opened up. Since I had expressed that I wasn't really into people management, I was given the opportunity to be a hybrid team lead -- giving technical guidance, direction and planning for the team, but not having to do the meaty people management stuff (like holding 1:1s, performance reviews, that sort of thing. I would be able to still do some hands on coding with the remaining time that I had.). The Senior Director of Engineering offered to run those 1:1s himself for my team members directly, so my team would all officially direct-report to him instead.&lt;/p&gt;

&lt;p&gt;Even then, I learned a lot about team leadership from my brief time there (I was in that role for only about a year until I moved for personal reasons -- mostly to switch to a fully remote company, instead of working in San Francisco any longer.). I must admit that having been an IC for a decade+ before this, it truly had not occurred to me what all the things are that a team lead is responsible for and why team leads are apparently perpetually busy and hard to reach. Why are they never available even though they barely get any code done?&lt;/p&gt;

&lt;h4&gt;
  
  
  What does a team lead do?
&lt;/h4&gt;

&lt;p&gt;Let's look at some scenarios. We could get started with the various leadership meetings you'd be regularly in. Those take up a fixed amount of your work days every day -- whether it's project planning, meeting with business operations teams about their needs, or with product managers on consumer product (I've had all of these regularly). But that's the typical stuff you already know about. Let's talk about the remaining hours of your day outside of those meetings instead. Say you run a team of 5, and 4 of them have one question for you that day. Each question takes you anywhere from 15-30 minutes to answer and/or starting a conversation; whether it's because there's some issues they need help figuring out, or because they need some context in feature development, or some direction in design approaches. There goes most of your day. You only had 2-3 hours of non-meeting time to begin with, so those are the same time slots your team will reach out to you because they can actually find you at your desk.&lt;/p&gt;

&lt;p&gt;Gaining insights and a new appreciation of team leads and their actual work is just scratching the surface of it. Then there's the real challenging part of being a team lead, which is dealing with humans. This is not about people management; but about actual tech discussions and direction. Working in a company of any reasonable size, you'll inevitably work with many different personalities on the team. Being a tech lead is not about ruling with an iron fist, that this is the system architecture you've designed, and that everyone is doing it this way now, or having everyone develop systems you designed. On any team with intelligent engineers, from a team of 5 you'll get 5 different ideas and system designs to solve a business problem. Your job as a tech lead is not just to evaluate the ideas based on merit, business context, time-frame; but also to facilitate discussion and narrow down on a solution design, while keeping everyone happy and productive and making sure no one feels like they haven't been heard or respected; all the while making sure progress is made in a timely manner (i.e. &lt;em&gt;quickly&lt;/em&gt;) and &lt;strong&gt;moving forward&lt;/strong&gt; in general! Think about this again. This isn't people management work; this isn't holding 1:1 meetings or performance reviews. This is the work that a tech lead does.&lt;/p&gt;

&lt;h2&gt;
  
  
  On working with people
&lt;/h2&gt;

&lt;p&gt;The experience I described above echoes the following excerpt from the article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;They still need the full manager toolset.  They’ll need to know how to rally people and teams and motivate them, or how to triage and restart a stalled project that everybody dreads. They still need to connect the dots between business objectives and technical objectives, and break down big objectives into components. They need to be able to size up a junior engineer’s ability and craft a meaningful assignment, one that pushes their boundaries without crushing them … then do the same for another twenty contributors. This is management work, from the slightly shifted perspective of “Get Thing X Done” not “care for these people”.&lt;/p&gt;

&lt;p&gt;So these tech leads usually spend more time in meetings than building things, and they will bitch about it but do it anyway, because writing code is not the best use of their time.  Tech is the easy part, herding humans is the harder part.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;I feel that one of the more important and more delicate aspect for a tech lead to "herd humans" (in order to "get shit done") is to both know the personalities of your team members very well, as well as utilizing their strengths correctly.&lt;/p&gt;

&lt;p&gt;Some of your team members come from backgrounds that have taught them to build for the proper engineering solution, for the long term, not incur tech debt. Sometimes, with the business context you have as a tech lead, you know that speed is necessary and sacrifices are needed. You have to juggle this delicate balance, while keeping the team happy. In other cases, it might be the total opposite. You got a builder in your team that's about coding up features as quickly as possible; so you'll want to encourage proper unit testing, documentation, and thinking of edge cases.&lt;/p&gt;

&lt;p&gt;Some of your team members will have strong opinions. They may be right a lot of times, and sometimes they may be right only because of the context and background they came from. Either way, they are imposing strong opinions on other team members. In cases like this, your job may be to keep an open mind and listen and take what is appropriate. In some cases you may have to hold your urge to defend your solution in a technical debate (that most engineers tend to do); and sometimes you might just be surprised how much merit the idea/design of someone with the opposite opinion of yours actually has. Your job is to keep things moving, and keep things moving &lt;em&gt;forward&lt;/em&gt;; and not dwell on "who wins" the technical debate.&lt;/p&gt;

&lt;p&gt;Sometimes less mature engineers like to debate for the sake of debate. Your/other team members' solutions are not wrong, but they like to point out what the issues are, what edge cases are not being covered, and so on. In those cases, know to pick your battle and let folks take the position they insist on but keep things moving forward. You'll notice that's something I'm repeating, because that's really the key here -- move things forward and get things done.&lt;/p&gt;

&lt;p&gt;Of course, in yet another scenario, you could have a team member who has too weak of opinions but have brilliant thoughts. It's also your job to draw out ideas from these folks.&lt;/p&gt;




&lt;p&gt;Ultimately, being an effective tech lead is about getting things done. It's both about getting things done yourself, and making sure that your team gets things done (and coherently). In some cases that aligns with proper engineering. Sometimes it doesn't. And even when it doesn't, it doesn't necessarily mean you go cowboy all the way. There's a balance to strive for. In certain other scenarios it could align with cowboying code. There's just some things and some time, in a business, where the thing has to ship tomorrow. You have to understand the context fully and the consequences fully and know what needs to be done. That's the job of a tech lead.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>webdev</category>
      <category>writing</category>
    </item>
    <item>
      <title>Microservices and Monorepo, React and jQuery/no frameworks</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Sun, 24 May 2020 22:44:07 +0000</pubDate>
      <link>https://dev.to/bigi/microservices-and-monorepo-react-and-jquery-no-frameworks-2aa9</link>
      <guid>https://dev.to/bigi/microservices-and-monorepo-react-and-jquery-no-frameworks-2aa9</guid>
      <description>&lt;p&gt;The debates of microservices vs monorepo never really end, with articles arguing over one side or another. I came across &lt;a href="https://medium.com/swlh/stop-you-dont-need-microservices-dc732d70b3e0"&gt;this article against microservices&lt;/a&gt; the other day, and while it has some very valid points and yes, if you're starting a new company or a new project and say "I know! I'll use microservices as backend for this", it's probably the wrong call; I'd probably argue that at a certain point in the growth of an organization, microservices begin to make sense.&lt;/p&gt;

&lt;p&gt;While thinking about that, that got me thinking -- that kind of statement is really not unlike the argument for React vs not-react (the framework-less option usually being jQuery with some static HTML) in frontend.&lt;/p&gt;

&lt;p&gt;If you're starting a hobby project and you just want to build an MVP, using React is probably overkill, unless you're working on a highly interactive web app (dashboards, charts, etc.). For most web sites with some minimal set of interactivities, static HTML and jQuery is probably sufficient.&lt;/p&gt;

&lt;p&gt;The difference between the React vs jQuery question in frontend and the Microservices vs Monorepo question in backend is probably the point on the spectrum at which it makes sense to switch. For frontend, once you get a certain size of a frontend web app (that isn't even very big), it probably starts to make sense to start using a framework already (although for myself, I'd definitely prefer Svelte over React). For backend, you can go a looooong way on a monorepo setup before moving to microservices. Many legit tech startups still run fully monorepo and that's completely valid.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>architecture</category>
      <category>react</category>
    </item>
    <item>
      <title>MS Edge's Maps behavior with scroll</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Fri, 15 May 2020 06:59:45 +0000</pubDate>
      <link>https://dev.to/bigi/ms-edge-s-maps-behavior-with-scroll-6f1</link>
      <guid>https://dev.to/bigi/ms-edge-s-maps-behavior-with-scroll-6f1</guid>
      <description>&lt;p&gt;I typically use a mix of Chrome and Firefox in my day to day computing, on both Mac and Windows. Once in a while when I'm on my personal Windows laptop, I click on one of those Win10 pre-login screen with pretty scenery and see where it is. It opens up a Bing result page on MS Edge. Then I click on the Bing Maps page to see where that place is.&lt;/p&gt;

&lt;p&gt;It catches me off-guard every time I do this that when I scroll (mouse scroll, or trackpad scroll with two fingers), it scrolls the map instead of zooming in and out.&lt;/p&gt;

&lt;p&gt;This is one of those cases where, yeah, maybe it makes more sense that when you "scroll" in a mouse or trackpad, the map scrolls, but the pre-established convention is different and it just doesn't feel good as a UX. It's like on the NES, once in a while you run into a game where A is shoot and B is jump. It's weird and it doesn't feel great.&lt;/p&gt;

&lt;p&gt;By the way, this isn't a Google Maps or Bing Maps difference, it's a browser difference. Google Maps and Bing Maps behave the same on Chrome and Firefox -- in both cases, scrolling on trackpad will zoom. On MS Edge, both Google Maps and Bing Maps will cause the map to scroll when you scroll on trackpad.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bootstrapping a Svelte project</title>
      <dc:creator>Bigi Lui</dc:creator>
      <pubDate>Tue, 28 Apr 2020 04:32:10 +0000</pubDate>
      <link>https://dev.to/bigi/bootstrapping-a-svelte-project-4hfm</link>
      <guid>https://dev.to/bigi/bootstrapping-a-svelte-project-4hfm</guid>
      <description>&lt;p&gt;In my &lt;a href="https://dev.to/bigi/big-2-card-game-svelte-jam-stack-4k08"&gt;last article&lt;/a&gt; I mentioned starting on a card game web app using Svelte as a frontend. (By the way, it may sound weird to use an app frontend framework for a game, but I think for a card game it works fine. It's not like I need a physics engine or anything ;)&lt;/p&gt;

&lt;p&gt;As I learn more about Svelte, I want to write some articles on some key learnings I have along the way.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's Svelte?
&lt;/h3&gt;

&lt;p&gt;Most frontend devs are familiar with React and Vue. Svelte is another framework in the same category. Its focus is on reducing boilerplate code, using vanilla js, and speed (by virtue of not having a virtual DOM). Its syntax is heavily borrowed from Vue, with each component looking something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * Programmatic logic about the component, in vanilla JS
   */&lt;/span&gt;
  &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/script&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;/**
   * CSS of the component here
   */&lt;/span&gt;
  &lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;example&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="nx"&gt;f00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/style&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;example&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Hello&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nx"&gt;Once&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="nx"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;you&lt;/span&gt; &lt;span class="nx"&gt;will&lt;/span&gt; &lt;span class="nx"&gt;see&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;alert&lt;/span&gt; &lt;span class="nx"&gt;box&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="nx"&gt;seconds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How do I start a Svelte project?
&lt;/h3&gt;

&lt;p&gt;Right from the &lt;a href="https://svelte.dev/"&gt;Svelte homepage&lt;/a&gt;, you will see a few cli commands that let you start a project from the &lt;a href="https://github.com/sveltejs/template"&gt;sveltejs/template&lt;/a&gt;. It's a great place to start for a first project. It starts you off with just one Svelte component on one page, with a default rollup config. &lt;a href="https://rollupjs.org/guide/en/"&gt;Rollup&lt;/a&gt; is the bundler Svelte suggests, but you can use other bundles if you like.&lt;/p&gt;

&lt;p&gt;But once you've started from the template and begin coding, you may quickly come to a question...&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I do routing?
&lt;/h3&gt;

&lt;p&gt;Because the template starts you off with one page and one component, you'll quickly wonder how best to do routing if you're trying to build a web app of any reasonable size (not just one page).&lt;/p&gt;

&lt;p&gt;From what I've gathered, there are a number of options of popular libraries to do routing with, with different goals in mind.&lt;/p&gt;

&lt;p&gt;Probably the most feasible one for a bigger web app is &lt;a href="https://sapper.svelte.dev/"&gt;Sapper&lt;/a&gt;. As their &lt;a href="https://sapper.svelte.dev/docs"&gt;doc&lt;/a&gt; mentions, it's heavily inspired by Next.js. Sapper is opinionated and basically gives you a &lt;code&gt;src/routes&lt;/code&gt; directory where you put your pages, with each page being a component. (Should be familiar for those who come from React/Next.js) Sapper can easily be configured to be used as a static site generator (JAM stack), or a standard server-side-rendering setup with a nodejs server. When you go with Sapper, you will want to use the &lt;a href="https://dev.tosveltejs/sapper-template"&gt;sapper-template&lt;/a&gt; instead of the &lt;code&gt;sveltejs/template&lt;/code&gt; to get started, as their &lt;a href="https://sapper.svelte.dev/docs#Getting_started"&gt;getting started&lt;/a&gt; guide mentions.&lt;/p&gt;

&lt;p&gt;(Funnily enough, remember that in my &lt;a href="https://dev.to/bigi/dev-to-is-the-perfect-demonstration-of-how-content-based-web-apps-should-be-developed-5fa4"&gt;first dev.to article&lt;/a&gt; I mentioned how one of the secret sauces of dev.to being so fast was because of its use of InstantClick, i.e. preloading links on mouse hover? Sapper comes with this functionality built in as well.)&lt;/p&gt;

&lt;p&gt;As you can imagine, Sapper does add a considerable amount of complexity to your Svelte app. If you're a veteran frontend developer coming from React and Next.js, this won't be an issue for you.&lt;/p&gt;

&lt;p&gt;What if you wanted to build a simpler app? In my case, I am building a simple card game, with a landing page and a game page. The game page is hash id based for different "game rooms"; and it contains a lot of dynamic interactivity on it.&lt;/p&gt;

&lt;p&gt;For this use case, I've found the &lt;a href="https://github.com/ItalyPaleAle/svelte-spa-router"&gt;svelte-spa-router&lt;/a&gt; library to be an excellent option. It only supports client-based routing (with a &lt;code&gt;#&lt;/code&gt; hash) and does not do server-side-rendering. However, you can easily run a JAM stack setup with the static site generator setup that comes with svelte template. I basically start from the &lt;code&gt;sveltejs/template&lt;/code&gt;, add &lt;code&gt;svelte-spa-router&lt;/code&gt; as a dependency into my &lt;code&gt;package.json&lt;/code&gt; and start from there.&lt;/p&gt;

&lt;h3&gt;
  
  
  You're in a good place to start coding
&lt;/h3&gt;

&lt;p&gt;With a Svelte project fully set up along with a router library of your choice, you should be ready to start coding away your new app frontend. Enjoy!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>svelte</category>
    </item>
  </channel>
</rss>
