<?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: Michael Salim</title>
    <description>The latest articles on DEV Community by Michael Salim (@michaelsalim).</description>
    <link>https://dev.to/michaelsalim</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%2F797679%2F4a630183-6441-4b16-b2b9-d64d120182d6.jpeg</url>
      <title>DEV Community: Michael Salim</title>
      <link>https://dev.to/michaelsalim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/michaelsalim"/>
    <language>en</language>
    <item>
      <title>Don't fall in love with your code</title>
      <dc:creator>Michael Salim</dc:creator>
      <pubDate>Sat, 07 Dec 2024 23:29:00 +0000</pubDate>
      <link>https://dev.to/michaelsalim/dont-fall-in-love-without-your-code-3f4k</link>
      <guid>https://dev.to/michaelsalim/dont-fall-in-love-without-your-code-3f4k</guid>
      <description>&lt;p&gt;I just deleted hundreds of lines of code I wrote yesterday and replaced them with 32 lines of new code. This was for a feature for &lt;a href="https://github.com/Vija02/TheOpenPresenter" rel="noopener noreferrer"&gt;TheOpenPresenter&lt;/a&gt;, used to indicate if an audio is playing.&lt;/p&gt;

&lt;p&gt;Every so often, I’d work on a functionality that seems quite straightforward to implement. In this case, I just needed to show this icon when audio is playing.&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%2Fqmniilbdpzamuq6dt2dv.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%2Fqmniilbdpzamuq6dt2dv.png" alt="Sample" width="199" height="116"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simple enough. Each of these is a scene containing multiple plugins. Each plugin has its own property like &lt;code&gt;isPlaying&lt;/code&gt; . We can merge the values between the plugins, and if the flag is true, we can show the icon.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecting a solution
&lt;/h2&gt;

&lt;p&gt;The main issue is how to access this data. See, we could access the data directly. But each plugin can have its own schema. While some plugins may have a simple &lt;strong&gt;isPlaying&lt;/strong&gt; property, some others might need something more complicated to represent its playing status.&lt;/p&gt;

&lt;p&gt;Simple, why not allow the plugin to register a callback/function that returns the state? &lt;/p&gt;

&lt;p&gt;This is the same pattern that TheOpenPresenter uses for many of its plugins. And while we’re at it, we can abstract it into a &lt;strong&gt;SceneState&lt;/strong&gt; object. So if we ever need any other state, we can add it here. Here’s how it might look like for the plugin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// The pattern we use for plugins&lt;/span&gt;
&lt;span class="nx"&gt;serverPluginApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onPluginDataCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pluginName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onPluginDataCreated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;serverPluginApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onPluginDataLoaded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pluginName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onPluginDataLoaded&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;serverPluginApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerRemoteViewWebComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;pluginName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;remoteWebComponentTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Example of how the new API might look like&lt;/span&gt;
&lt;span class="nx"&gt;serverPluginApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerSceneState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;pluginName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rendererData&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;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;audioIsPlaying&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="nx"&gt;rendererData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPlaying&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Server handling
&lt;/h2&gt;

&lt;p&gt;Notice that the code above is handled on the server. This is because TheOpenPresenter consists of 3 separate components: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Remote - where this audio indication is shown&lt;/li&gt;
&lt;li&gt;The Renderer - plays the audio&lt;/li&gt;
&lt;li&gt;The Server - connects the two&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ideally, we should handle this in the frontend (remote) so that we don’t add extra load to the server. However, registering this function can be messy. Our frontend uses a micro-frontend architecture loaded with Web components.&lt;/p&gt;

&lt;p&gt;The red area below is a React shell. The green area is loaded through web components and is managed by each plugin. &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%2Fjwudpscp9664n7dmxced.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%2Fjwudpscp9664n7dmxced.png" alt="Shell" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the audio icon is located on the left side of the shell. How do we provide the function that we need to the shell? We could include a JS function in the web component bundle but that sounds like a mess in the long term.&lt;/p&gt;

&lt;p&gt;Handling this on the server seems like the proper way to do this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Plumbing the data through
&lt;/h2&gt;

&lt;p&gt;With that decided, it’s time for implementation. There are a few things to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create the plugin API &lt;/li&gt;
&lt;li&gt;Provide this data to the frontend&lt;/li&gt;
&lt;li&gt;Consume and update the UI&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementing
&lt;/h3&gt;

&lt;p&gt;I won’t bore you with the details so here’s an overview. The API was not quite straightforward since our data can be quite confusing. In short: A scene can have multiple plugins. And there can be multiple renderers, each viewing a scene in a different way. So a plugin could have multiple renderers showing it in different ways. But with a bit of data manipulation, problem solved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Consuming and updating the UI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consuming the value was straightforward. I contemplated using Yjs’s awareness protocol to provide the data since it’s real-time and the framework is already in place. This is how the state is stored. However, including this data from the server is its own problem. So I decided to use GraphQL instead - the protocol we’re using for everything else in the platform.&lt;/p&gt;

&lt;p&gt;So all we need to do is call the endpoint, listen to it using GraphQL’s subscription, and show the icon as needed. Done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Providing this data to the frontend&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Thankfully, we use &lt;a href="https://postgraphile.org/" rel="noopener noreferrer"&gt;Postgraphile&lt;/a&gt; which makes extending the GraphQL schema quite straightforward. We can also make it a subscription simply by adding  &lt;code&gt;@pgSubscription&lt;/code&gt; to the GraphQL schema. It’ll then watch a topic and update the value whenever we call &lt;code&gt;pg_notify&lt;/code&gt; on that topic. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pgPool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s2"&gt;`select pg_notify('graphql:sceneState:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;','{}');`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="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;Manipulating the data was annoying but just a bit of patience and it’s done!&lt;/p&gt;

&lt;p&gt;The last piece of the puzzle is calling &lt;code&gt;pg_notify&lt;/code&gt; when we need to.&lt;/p&gt;

&lt;p&gt;For this, we can add a listener to the state (Yjs) and call the notify whenever anything changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observeDeep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Call pg_notify here&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only thing left to do is performance improvements. Right now, the function is called for every small change, it’s also updated to the frontend. We can calculate the resulting state and compare if anything changes before pushing the update.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better solution
&lt;/h2&gt;

&lt;p&gt;Now this solution certainly works. But I hated that we were listening to every single change. It’s unnecessary and I’m not sure how the performance will scale. Is there any better solution?&lt;/p&gt;

&lt;p&gt;So I stepped back for a second and an idea came: &lt;strong&gt;How about we go to the basics and use the data from Yjs?&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;The problem was that each plugin may use different ways to indicate play status. So we needed a way to know how to calculate the resulting state ourselves. But rather than letting the user pass a function, &lt;strong&gt;why not reserve a property that they can use to indicate this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Rather than passing a function to calculate the state, each plugin could set the reserved state directly alongside their existing data with properties like &lt;code&gt;__audioIsPlaying&lt;/code&gt; . They could use this value directly, or they could keep it in sync with their existing properties like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onRendererDataLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;rendererData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;watchYjs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="c1"&gt;// Watch the isPlaying property&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isPlaying&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// And if it changes, sync the __audioIsPlaying property&lt;/span&gt;
      &lt;span class="nx"&gt;rendererData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;__audioIsPlaying&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rendererData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;isPlaying&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deleting old code
&lt;/h2&gt;

&lt;p&gt;The new method is brilliant. No extra listener, no extra API, just a simple reserved property.&lt;/p&gt;

&lt;p&gt;The cost? Well, I already wrote 95% of the first implementation 🫣&lt;/p&gt;

&lt;p&gt;&lt;em&gt;**“It’ll be such a shame to delete this when I’ve worked on it for so long. Everything else is perfect other than this one thing!”&lt;/em&gt;* - My mind*&lt;/p&gt;

&lt;p&gt;This is not my first time. Not second or third either. This time it was just a few hours of work. The longer it takes to implement, the harder it is to let go. But if we should not get attached to servers, we shouldn’t be attached to the code we write either.&lt;/p&gt;

&lt;p&gt;It’s obvious that the second implementation is better. It’s faster, less moving parts, less API surface, and less code to maintain. The &lt;a href="https://github.com/Vija02/TheOpenPresenter/commit/485fd2ff4d4dc1fdb1e010da2fe71d9bdc7ca45f" rel="noopener noreferrer"&gt;first implementation&lt;/a&gt; added 289 new lines while the &lt;a href="https://github.com/Vija02/TheOpenPresenter/commit/8c72633158debf9dcb54e826e61cb69b31a304e7" rel="noopener noreferrer"&gt;second implementation&lt;/a&gt; only added 32 new lines. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What’s the lesson to learn then?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, maybe find the simplest solution first. But sometimes we don’t reach the best solution just by thinking about it. If that’s the case, &lt;strong&gt;don’t love your code and don’t be afraid to throw it away&lt;/strong&gt;. And maybe write a blog post so that you get something out of it!&lt;/p&gt;




&lt;p&gt;If you're read this far, you might want to try &lt;a href="https://theopenpresenter.com/" rel="noopener noreferrer"&gt;TheOpenPresenter&lt;/a&gt;! It's an open-source presentation system that lets you control any of your screens remotely.&lt;/p&gt;

&lt;p&gt;Show slideshows, play videos, use as dashboards and many more. The software is still very early in development as you can tell from this post but it's stable enough to use regularly. I personally use it for my meetups every week.&lt;/p&gt;

&lt;p&gt;Any questions, do ask. Or feel free to report issues in the &lt;a href="https://github.com/Vija02/TheOpenPresenter" rel="noopener noreferrer"&gt;Github repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>webdev</category>
      <category>programming</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I have a new Junior Developer and it kinda sucks</title>
      <dc:creator>Michael Salim</dc:creator>
      <pubDate>Thu, 11 May 2023 20:09:49 +0000</pubDate>
      <link>https://dev.to/michaelsalim/i-have-a-new-junior-developer-and-it-kinda-sucks-pbl</link>
      <guid>https://dev.to/michaelsalim/i-have-a-new-junior-developer-and-it-kinda-sucks-pbl</guid>
      <description>&lt;p&gt;That developer is ChatGPT and I have a love-hate relationship with it.&lt;/p&gt;

&lt;p&gt;There are so many articles about AI nowadays so I’ll try to keep this one short.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using ChatGPT to code is like having a Junior developer under you. They can get the code 80% there but you’ll need to review practically every line.&lt;/p&gt;

&lt;h2&gt;
  
  
  The scenario
&lt;/h2&gt;

&lt;p&gt;I have to admit that there are plenty of ways you could use ChatGPT to help write code. It’s probably better at some of them and worse at others.&lt;/p&gt;

&lt;p&gt;For example, I’ve heard plenty of people using it as a search engine. I personally find that a bit questionable. It’s not that hard to find documentation or even answers through search. I use &lt;a href="https://kagi.com/"&gt;Kagi&lt;/a&gt; which helps to cut down the fluff. On the contrary, using ChatGPT, how are you supposed to know if the answer is true?&lt;/p&gt;

&lt;p&gt;On the other side of the spectrum, there are plenty of people using it as a glorified auto-completion. Something like &lt;a href="https://github.com/features/copilot"&gt;Github Copilot&lt;/a&gt;. I can see that being useful.&lt;/p&gt;

&lt;p&gt;The scenario I’ve chosen is:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Converting my code from &lt;a href="https://chakra-ui.com/"&gt;chakra-ui&lt;/a&gt; to &lt;a href="https://tailwindcss.com/"&gt;tailwindcss&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;There are plenty of resources for both technologies, especially Tailwind (and of course, HTML). It should have plenty of data to work from.&lt;/li&gt;
&lt;li&gt;It is very possible to create a program to do this. While there may be plenty of edge cases, the functionality itself is straightforward. chakra-ui is inspired by tailwind so it inherits a bunch of things that can be easily transferred.&lt;/li&gt;
&lt;li&gt;It is something I need to do. So it’s a real use case that someone else might encounter.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prompt engineering is a chore
&lt;/h2&gt;

&lt;p&gt;Looking around the field, it seemed like prompting well is the difference between getting good or mediocre results. I’m not the best at this but I did some research and tried my best.&lt;/p&gt;

&lt;p&gt;I made sure to use multiple prompts to get it to a good state. Only pasting my code whenever it seems like it knows it should only convert my code.&lt;/p&gt;

&lt;p&gt;Honestly, this part could be improved. Between the UI and the interaction, it’s quite a chore to use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It can take some time for it to reach the desired state&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Between me thinking what to write, then waiting for it to respond and finish writing, it gets quite annoying to do this work&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It’s inconsistent&lt;/strong&gt; &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It doesn’t help that you can’t always reuse the same prompts. ChatGPT has some randomness to it so I had to tweak the prompt every single time. Copy-pasting a prompt that worked previously didn’t help.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It’s very easy to get out of the desired state&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The above is fine if it’s a one-time thing. But I found that it’s very easy for the AI to forget everything after the first code conversion. The most repeated success I had was 3 times I believe. Beyond that, it returned completely unrelated things like explaining what my code does.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Token is quite a limiting factor&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s only so much code that it can produce due to its token limit. You can check the &lt;a href="https://platform.openai.com/tokenizer"&gt;tokenizer here to see how it calculates the tokens&lt;/a&gt;. It’s not very friendly for code. Below is an example of one of its outputs. Each different color represents a token.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Fz4SLWNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/saaxwj6jx6n93u7ickqv.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fz4SLWNn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/saaxwj6jx6n93u7ickqv.jpg" alt="Tokenizer" width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because of this, I have to split the conversion into multiple steps. Otherwise, the generation will stop in the middle. Telling it to continue doesn’t work most of the time. Combined with the points above, this quickly becomes a chore to do.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1BmSAQIu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nb0so616468ogvguc9g.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1BmSAQIu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nb0so616468ogvguc9g.jpg" alt="Continue error" width="540" height="315"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;All in all, the feeling I get is that I don’t want to write English. If I already have to write something, I’d rather directly write code that works.&lt;/p&gt;

&lt;p&gt;I can see prompt engineering being a key skill to have in the future. Similar to how knowing how to search is a key skill in today’s world.&lt;/p&gt;

&lt;h2&gt;
  
  
  The output is not too bad
&lt;/h2&gt;

&lt;p&gt;For all the work it took, ChatGPT did output some pretty impressive code. Here’s some comparison between the original and the result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ijpUIIBr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kunffunbrkwej9e4flcp.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ijpUIIBr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kunffunbrkwej9e4flcp.jpg" alt="Comparison 1" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--grChe38B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzk66a50ob996d97z2pj.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--grChe38B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rzk66a50ob996d97z2pj.jpg" alt="Comparison 2" width="800" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it did the job decently well.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Good&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The structure is brought over pretty well&lt;/strong&gt;&lt;br&gt;
Other than the details, the HTML structure is quite similar to the original. The spacings are off but the overall layout remains.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The text content doesn't change&lt;/strong&gt;&lt;br&gt;
The content itself was handled well. Even though I had to double-check all of them, none of it were changed. The only exception are those derived from JS.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Bad&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It had random classes&lt;/strong&gt;&lt;br&gt;
There are plenty of classes that don't do anything. Some were changed and some others were invalid tailwind classes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;There are plenty of tiny differences&lt;/strong&gt;&lt;br&gt;
The UI looked as if a library was just upgraded and someone forgot to migrate the code. In this case, it’s because ChatGPT randomly changed the values of the classes. For example, a &lt;code&gt;padding&lt;/code&gt; of 3 to 4, or changing the &lt;code&gt;font-weight&lt;/code&gt; from bold to normal. The details were all wrong.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;It doesn’t work half the time&lt;/strong&gt;&lt;br&gt;
The screenshots above were those that I can directly compare. In reality, the results were either incomplete or just wrong enough that I had to make many changes to get it to work. And because of that, I can’t compare it side by side. There’s simply nothing I can compare with since the code doesn’t run.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  It’s like reviewing PRs instead of writing code
&lt;/h2&gt;

&lt;p&gt;What’s great about this is that I have something to work from. Thankfully, the work itself is very straightforward. It’s easy to spot errors. The mapping between the classes from chakra-ui and tailwind is almost one-to-one. It’s only tedious because the syntax is different.&lt;/p&gt;

&lt;p&gt;But this approach is more akin to reviewing PRs. I had to scan through the whole code to make sure everything is done correctly.&lt;/p&gt;

&lt;p&gt;It looks alright at first glance. But there are many gotchas once you work with it and pay attention closely. It brings this false sense of security which made me a bit frustrated once I found out about all the flaws.&lt;/p&gt;

&lt;p&gt;A big part of it is code that is interpolated from other codebases. It doesn’t look terrible but it’s not what I wanted.&lt;/p&gt;

&lt;p&gt;There are even some alarming changes like changing the heading from h2 to h1. I can only assume it did this because of the content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;u&gt;It felt like a junior developer slapped something together and didn’t test their code&lt;/u&gt;&lt;/strong&gt;. And now I have to review and fix it without being able to tell them to fix it themselves. And this is awful. I already have my fix of reviewing PRs at work. Now I have to do it too for my own projects? No thanks!&lt;/p&gt;

&lt;h2&gt;
  
  
  Hopeful future
&lt;/h2&gt;

&lt;p&gt;While I don’t think it’s anywhere near there yet, I do think AI will be helpful at some point for development in the future. Whether the answer is LLMs, who knows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Token limits are constantly increasing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’ve held off finishing this article long enough. During that time, there are plenty of announcements of token limit being increased. While I’m not sure if it’s public, paid or whatever, this is great to see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompting UX should get easier with time&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With ChatGPT plugins and better integration, issues like remembering state &amp;amp; consistency should be improved. Hopefully, that’ll lower the effort needed to get useful output from ChatGPT.&lt;/p&gt;

&lt;p&gt;In the meantime, I completed the other half of the migration manually. Was it faster? Well, who knows. It felt about the same. But writing it manually did feel a whole lot better.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>chatgpt</category>
      <category>tailwindcss</category>
      <category>automation</category>
    </item>
    <item>
      <title>Making YouTube video with React</title>
      <dc:creator>Michael Salim</dc:creator>
      <pubDate>Fri, 24 Feb 2023 23:08:19 +0000</pubDate>
      <link>https://dev.to/michaelsalim/making-youtube-video-with-react-513m</link>
      <guid>https://dev.to/michaelsalim/making-youtube-video-with-react-513m</guid>
      <description>&lt;p&gt;I recently started a YouTube channel. It's the educational kind where I explain cool technologies. My inspiration was channels like &lt;a href="https://www.youtube.com/@Wendoverproductions" rel="noopener noreferrer"&gt;Wendover Productions&lt;/a&gt; and &lt;a href="https://www.youtube.com/@KentoBento" rel="noopener noreferrer"&gt;Kento Bento&lt;/a&gt;. One thing I want to get right is animations. I have previously watched a lot of videos by &lt;a href="https://www.youtube.com/@3blue1brown" rel="noopener noreferrer"&gt;3Blue1Brown&lt;/a&gt;. Their animation is very useful for explaining complex topics.&lt;/p&gt;

&lt;p&gt;The problem is that I’ve never used tools like After Effects before. I also don’t want to learn or pay for it. Actually, I can’t even use it since I’m running Linux.&lt;/p&gt;

&lt;p&gt;Being a “good” programmer, I thought — why not make the animation in React? What could go wrong?&lt;/p&gt;

&lt;h2&gt;
  
  
  React is my home turf
&lt;/h2&gt;

&lt;p&gt;Now, I wanted something programmable. Somewhere you can tweak text and numbers instead of manually aligning elements. I don’t want to copy-paste hundreds of times when it could be a simple loop. And the idea of reusing past components is appealing to me.&lt;/p&gt;

&lt;p&gt;Why React though? Well, it’s what I’m most familiar with. All my projects are built using it and I can create layouts with CSS pretty quickly. In fact, I’ve created &lt;a href="https://github.com/Vija02/LD39" rel="noopener noreferrer"&gt;a game with React + CSS Grid&lt;/a&gt; for a game jam in the past.&lt;/p&gt;

&lt;p&gt;And most importantly, it’s fun!&lt;/p&gt;

&lt;h2&gt;
  
  
  The alternative was painful
&lt;/h2&gt;

&lt;p&gt;For comparison's sake, I animated a simple scene in &lt;a href="https://www.blackmagicdesign.com/products/davinciresolve/" rel="noopener noreferrer"&gt;Davinci Resolve&lt;/a&gt; using a few tutorials from YouTube. This is the result:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/b0xVjE_0zOU"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;That’s it. But for such a simple scene, it was a major pain to create. Here’s what the node graph looks like. Even worse, if I want to change a group of items slightly, I have to update them separately.&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%2Fchtz6mvwvta515vogy4m.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%2Fchtz6mvwvta515vogy4m.png" alt="Davinci Resolve Node Graph" width="800" height="208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  First scene concept
&lt;/h2&gt;

&lt;p&gt;I had an image of what I wanted to illustrate. &lt;a href="https://www.youtube.com/watch?v=kOjgPTj9MKM" rel="noopener noreferrer"&gt;The video&lt;/a&gt; is about how the PS3 was used as a supercomputer. For that, a bunch of them were clustered into a single machine. I wanted to show how many PS3s were used. Showing a number is fine but I also wanted to show each physical machine.&lt;/p&gt;

&lt;p&gt;First, a single PS3 would appear, then a few dozen, then hundreds and then to its final amount. All while animating the previous ones to their new position. Here’s the result:&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/1Limi6qPg_M"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The project setup
&lt;/h2&gt;

&lt;p&gt;To bootstrap the project, I used Next.js. This part doesn’t matter much. But its file-based routing is convenient for separating different scenes. I can just create a new file for each scene.&lt;/p&gt;

&lt;h3&gt;
  
  
  Animating Library
&lt;/h3&gt;

&lt;p&gt;While researching for this, I had an idea of scenes I want to make. For example: Graphs, Pie charts and &lt;strong&gt;illustrating how a CPU work&lt;/strong&gt; with simple boxes. One of the ideas of using React is to leverage its large amount of open-source libraries.&lt;/p&gt;

&lt;p&gt;However, It turns out that most packages don’t handle animation very well. And if it has any, it’s likely not configurable. So I began looking for an animation library and came across Anime.js.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://animejs.com/" rel="noopener noreferrer"&gt;Anime.js&lt;/a&gt; is great because I can simply define the initial state and the goal. Anime.js will handle everything in between.&lt;/p&gt;

&lt;p&gt;Here’s a snippet of the final code:&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%2Ffs6rtfgo5qht4mi2nqr8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffs6rtfgo5qht4mi2nqr8.jpg" alt="PS3 Animation Code" width="800" height="413"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some maths involved to calculate where each PS3 image should go. However, the animation calculation is all done by Anime.js. Not bad.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recording &amp;amp; Exporting
&lt;/h3&gt;

&lt;p&gt;Now the harder part is finding a way to record and export the animation. I wanted to automate this process. Recording my screen sounded like a very crude way of doing this. Ideally, it should take its time to render each frame as it needs and create an mp4 file. This is so that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Even if the animation doesn’t run smoothly on my machine, it still will show up smooth on the recording&lt;/li&gt;
&lt;li&gt;The resolution will be correct. My screen is HD so I can capture that but what if I want a larger resolution? Recording this way would also take a whole monitor which is annoying.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Problems and Iterations
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;rrweb + rrvideo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the end, I had to experiment quite a few times. First, I tried using &lt;a href="https://github.com/rrweb-io/rrweb" rel="noopener noreferrer"&gt;rrweb&lt;/a&gt; since it was something that was already on my radar. The idea is I would record using that and convert it into a video using &lt;a href="https://github.com/rrweb-io/rrvideo" rel="noopener noreferrer"&gt;rrvideo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This approach was… alright. It’s an alternative to recording my screen. The advantage is that rrvideo spawns a separate chrome instance (through puppeteer). This allows the recording to run headlessly.&lt;/p&gt;

&lt;p&gt;The disadvantage is it brings a bunch of complexity to an otherwise simple task. The initial recording step is also an unnecessary process.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canvas w/ paper.js&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The previous Anime.js with HTML+CSS approach worked with a few elements. But it didn’t run very well once I added hundreds of images. Each PS3 was an image and rendering a thousand of them was not great for performance. I wasn’t able to get it running at 30 fps for the whole sequence.&lt;/p&gt;

&lt;p&gt;To solve that issue, I searched for some solutions using canvas. I didn’t want to work with pure canvas so after doing some research, I settled with &lt;a href="https://github.com/paperjs/paper.js" rel="noopener noreferrer"&gt;paper.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Using paper.js was convenient because it allows me to reuse the Anime.js logic I have previously written. Instead of modifying the DOM element directly, I modified it to update a JSON object. Then I use that object to update the canvas every tick.&lt;/p&gt;

&lt;p&gt;It’s not very elegant but it worked so good enough!&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%2Fdve3qmnsbwrrixvluihh.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdve3qmnsbwrrixvluihh.jpg" alt="Canvas Draw Code" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Canvas Capturing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I also tried to find a better recording method. Since I’m now using canvas, there are some solutions to recording that seemed quite elegant. For example, paper.js uses &lt;code&gt;requestAnimationFrame&lt;/code&gt; to achieve its smooth animation. There are libraries like &lt;a href="https://github.com/spite/ccapture.js/" rel="noopener noreferrer"&gt;ccapture.js&lt;/a&gt; that hooks into the various methods like &lt;code&gt;requestAnimationFrame&lt;/code&gt; and &lt;code&gt;setInterval&lt;/code&gt;, allowing it to render each frame separately.&lt;/p&gt;

&lt;p&gt;Using this approach works but it was quite troublesome to get working. For example, the animation loop needs to be started before the recording starts. This is quite annoying to deal with. It also means that it’s not possible to add a delay between animation easily.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recording manually was a good enough solution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With that problem in the way, I decided to record the screen manually instead. For this, running a simple &lt;code&gt;ffmpeg&lt;/code&gt; command was good enough to get the results I wanted. You can see the results in the 2nd video above.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizing other React libraries
&lt;/h2&gt;

&lt;p&gt;The next scene I had in mind was an animated pie chart. There are so many charting libraries around that I was sure one of them would fit my needs. The pie chart below was rendered using &lt;a href="https://github.com/recharts/recharts" rel="noopener noreferrer"&gt;recharts&lt;/a&gt;, utilizing its mount animation. To get the results, I recorded my screen like the first scene.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/ajDvzKAp26I"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  The actually useful animation!
&lt;/h2&gt;

&lt;p&gt;And finally, the reason I wanted my videos to be animated in the first place. Below is a simple illustration of the difference between how the CPU processes data compared to the GPU. It helps to illustrate the point instead of simply saying it in words.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/eEC-utT5_34"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;For this scene, I decided to research other ways to achieve this. The Anime.js method was decent but it was rather clunky particularly in terms of timing it. Meanwhile, the canvas method was performant but I’d rather use a “proper” tool if I can’t leverage React’s tooling or even CSS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Target based animation
&lt;/h2&gt;

&lt;p&gt;A bit more research led me to &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;Framer Motion&lt;/a&gt;. I’ve actually heard about the library in the past. I hadn’t used it since I never needed it. But I did experience it through other libraries like the excellent &lt;a href="https://chakra-ui.com/" rel="noopener noreferrer"&gt;Chakra UI&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;Like Anime.js, Framer motion lets you define the result you wanted. The library will then handle the animation part. However, Framer motion has the added benefit of having a nicer workflow. It’s all directly in React. In my React code, I can simply define what to render depending on a state. Then, I can play around with the state to change between scenes. Framer motion will handle the rest.&lt;/p&gt;

&lt;p&gt;After hooking the state value to key press events, I can control when I want to transition with a button press. This also allows me to match the animation with my video.&lt;/p&gt;

&lt;p&gt;Below is how the white blocks in my video that flow between the RAM and GPU is defined. Isn’t it nice? To start the animation I only need to render it. In this case, incrementing the &lt;code&gt;variant&lt;/code&gt; counter.&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%2Fad7batbcuh6x6dy7cx9b.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fad7batbcuh6x6dy7cx9b.jpg" alt="Framer Motion Code" width="800" height="691"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  CSS layout animation
&lt;/h3&gt;

&lt;p&gt;Another added benefit of Framer Motion is its layout animation capabilities. With Anime.js, I’m limited to animating linear values. But what if I added a new element or changed a flexbox property? It wouldn’t work at all. Thankfully, Framer Motion handles this use case nicely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transparency is a chore to deal with
&lt;/h3&gt;

&lt;p&gt;So that was the stack that I used for the rest of the animation. There are other details and methods that I had to deal with in the making of this video. Many of them are omitted since they never went anywhere.&lt;/p&gt;

&lt;p&gt;But one detail I had to work around is transparency. To get it working, I set the animation to have a green background. In Davinci Resolve, I can then mask/key the background away. This worked but it limited the colors I could use in my animation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Should you do it?
&lt;/h2&gt;

&lt;p&gt;Probably not. I’m sure there are good ways of doing this without code. Though I can’t recommend any since I’ve never tried them. But if you are someone who managed to read this far, then you're probably savvy enough to do something like this!&lt;/p&gt;

&lt;h2&gt;
  
  
  Future solution
&lt;/h2&gt;

&lt;p&gt;Now that I’m planning for a second video, there are other stuff that I’d like to try. For example: &lt;a href="https://motioncanvas.io/" rel="noopener noreferrer"&gt;Motion Canvas&lt;/a&gt;. This tool is authored by someone who was looking for something similar to me. He actually has a whole video explaining the tool. It included various features like timing the animation with audio and rendering it per frame. It also renders the animation as an image sequence. I’m assuming this would solve the transparency problem nicely.&lt;/p&gt;

&lt;p&gt;If you know other tools that solve this in a good way, do let me know!&lt;/p&gt;




&lt;p&gt;That's it for now. If you’re interested in the final video, you can watch it here: &lt;a href="https://www.youtube.com/watch?v=kOjgPTj9MKM&amp;amp;t=62s" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=kOjgPTj9MKM&lt;/a&gt;. If you like this article, you might like the contents of the video too.&lt;/p&gt;

</description>
      <category>gratitude</category>
    </item>
    <item>
      <title>I have 218 browser tabs open</title>
      <dc:creator>Michael Salim</dc:creator>
      <pubDate>Mon, 01 Aug 2022 22:12:00 +0000</pubDate>
      <link>https://dev.to/michaelsalim/i-have-218-browser-tabs-open-1la9</link>
      <guid>https://dev.to/michaelsalim/i-have-218-browser-tabs-open-1la9</guid>
      <description>&lt;p&gt;Yes, that number is correct. And it’s awesome! This wouldn’t be possible just a short few years ago.&lt;/p&gt;

&lt;p&gt;Everybody that heard that inevitably ask me, &lt;strong&gt;“But why do you have that many tabs open?”.&lt;/strong&gt; Well, let's start from the beginning.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;A little background&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;I've been on the internet for as long as I can remember. Maybe around 16 years? That’s probably less compared to many of you. But for me, that's more than 2/3 of my life.&lt;/p&gt;

&lt;p&gt;In that period, I went from using IE, then Chrome, to Firefox, back to Chrome and now I’ve settled with Firefox. The reason why is tightly related to why I have so many tabs open at any given time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5d6nfoi89iqin1ch6t8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo5d6nfoi89iqin1ch6t8.jpg"&gt;&lt;/a&gt;My browser progression (excluding minor temporary ones).&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Browser tabs changed the game&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By the time I browse regularly, tabs were already commonplace. I hated the times I had to use Internet explorer and open multiple windows. It was always messy and hard to navigate around.&lt;/p&gt;

&lt;p&gt;It's funny because you can argue it's not a big difference. You only move the location you click to change pages. Instead of clicking on the bottom of the screen, you click on the top instead. Meanwhile, windows allow you to place your pages wherever you want. So why are tabs so popular then?&lt;/p&gt;

&lt;p&gt;I believe the smallest of changes can make such a big difference in workflow and user experience. It’s similar to why I use &lt;a href="https://i3wm.org/" rel="noopener noreferrer"&gt;i3&lt;/a&gt;, a tiling window manager. More constraint and scoping can be very helpful. I might write a separate article about it in the future.&lt;/p&gt;

&lt;p&gt;No matter the reason, I think most will agree that &lt;strong&gt;tabs made people open more pages compared to window navigation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqcz2eq4ewi7ymreue5y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqcz2eq4ewi7ymreue5y.png"&gt;&lt;/a&gt;Example of browser tabs&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Opening many tabs would lag my PC&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;By this point, I was regularly opening tens of tabs. Specific activities also increase that number by a lot - like researching a topic.&lt;/p&gt;

&lt;p&gt;You know where this is going. My PC would lag. It would freeze randomly. Pages also start taking longer to load. Whoops, I’ve run out of memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;It’s hard to find the tabs you want&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Another issue is the lack of horizontal space. As you open more tabs, each tab continues to decrease in size. This is annoying to deal with. What if you’re doing some research and you open a bunch of random websites? You don’t know what their favicon looks like. Now you have to open them one by one to find the one you were looking for. &lt;/p&gt;

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

&lt;h2&gt;
  
  
  &lt;strong&gt;A new way of viewing tabs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;One day I found the &lt;a href="https://addons.mozilla.org/en-US/firefox/addon/tree-style-tab/" rel="noopener noreferrer"&gt;Tree Style Tab&lt;/a&gt; extension. It displays your tabs as a tree but more importantly, it’s &lt;strong&gt;vertical.&lt;/strong&gt; Not a big deal right?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35i65peyywpgfnufjfuq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35i65peyywpgfnufjfuq.jpg"&gt;&lt;/a&gt;Tree Style Tabs in action&lt;/p&gt;

&lt;p&gt;Turns out this entirely changed the game for me. Just like the change from windows to tabs enabled more tabs to be open, tree tabs did the same. Horizontal tabs are awful to manage after 15 tabs. &lt;strong&gt;With tree styles, you can easily manage more than 60 tabs with little to no issue.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In tree tabs, the titles are always visible no matter how many tabs you have open. When you open a link in a new tab, it will be nested within the previous tab indicating how you reached that page&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Navigation is king&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Around the same time, I also found the &lt;a href="https://github.com/philc/vimium" rel="noopener noreferrer"&gt;Vimium&lt;/a&gt; extension. It enables vim-like navigation in your browser. &lt;strong&gt;You can practically do any action you want with just your keyboard.&lt;/strong&gt; The best way to show you is to demo it.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/r7G2B8JgFFg"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;All of those actions were done with a keyboard. At the 20s mark, I used a shortcut to go back to the previous tab (basically alt-tab). And at 15s &amp;amp; 22s, you can see the ability to search for a tab. Combined with Tree style tabs, managing many tabs becomes a breeze.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;“But wouldn’t your PC lag all the time?”&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Thankfully &lt;strong&gt;&lt;a href="https://en.wikipedia.org/wiki/Moore%27s_law" rel="noopener noreferrer"&gt;Moore's law&lt;/a&gt;&lt;/strong&gt; is in full effect. While each web page keeps increasing in size, hardware increased its performance even more! CPU keeps increasing their core count and RAM gets cheaper each year (well, let’s ignore the chip shortage 😉). At the moment, I have 48GB of RAM to handle all my needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Even more tabs!&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You might’ve noticed a theme here. I found another extension! It’s called &lt;a href="https://github.com/mbnuqw/sidebery" rel="noopener noreferrer"&gt;Sideberry&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Sideberry looks very similar to Tree style tabs. However, it introduced a concept called panels. &lt;strong&gt;Each panel has its own list of tabs&lt;/strong&gt;. It allows you to compartmentalize your tabs into separate buckets.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqsqswfpk5nrlqarjin9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqqsqswfpk5nrlqarjin9.jpg"&gt;&lt;/a&gt;Panels in Sideberry&lt;/p&gt;

&lt;p&gt;On a typical day, I usually work on several projects. I would use a panel for each long-running project. One for entertainment and another for rabbit holes I happen to stumble upon. Each panel have about 50 tabs on average.&lt;/p&gt;

&lt;p&gt;So you can imagine how I get to 200+ tabs very easily. I believe I even reached 300 tabs once or twice before. Once I’m used to having about 70 tabs, I started to have more open from 100, 150, 200, and eventually 300. It doesn’t make that much of a difference.&lt;/p&gt;

&lt;p&gt;It does depend on what I’m doing. It’s currently a less busy time. And at this time of writing, I “only” have 151 tabs open. Not too bad!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Just because it’s there doesn’t mean it’s open&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you’re still not convinced that this works, there’s more! Sideberry also allows you to unload a page. An unloaded page doesn’t consume resources. When you open it, it will load the page from scratch. This means that you can have as many tabs open without having performance issues! Just make sure you unload some of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5vcld9movkgyu0i0lm0.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr5vcld9movkgyu0i0lm0.jpg"&gt;&lt;/a&gt;Sideberry unloaded tabs&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;&lt;em&gt;“Sure, the technology allows you to do it. But just because you can doesn’t mean you should!”&lt;/em&gt;&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;To that I say: Why shouldn’t I? Having these tabs gives me 2 huge benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I can see how I got to the pages I did when researching. From there I can easily close the tree when it doesn’t lead to good content.&lt;/li&gt;
&lt;li&gt;The tabs act as a reminder of things I want or need to work on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine browsing &lt;a href="https://news.ycombinator.com/" rel="noopener noreferrer"&gt;Hacker News&lt;/a&gt; and you stumble upon a gold mine. It had so many good resources that you can’t help but open all the links. Half of them turn out to be useless and you close them. Now you’re left with 4 things you want to know more about. So you decide to leave them open. Then you go through each of them, opening links and reading them.&lt;/p&gt;

&lt;p&gt;When you’re done, you can easily close the whole tree since they’re sorted as such. Now the other 2 pages are products you want to try. But you don’t have time for that right now. So you leave them open and continue with work on a separate Sideberry panel. When you’re free, you go back to the other panel and try the product. Turns out it was awesome and everyone lived happily ever after.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;em&gt;“Why not use bookmarks?”&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;I have 280 bookmarks. A good dozen for quick access links. Most are resources and cool things I might want to check again in the future. Maybe it’s an early access product and I want to check back once it’s more stable.&lt;/p&gt;

&lt;p&gt;It’s simply &lt;strong&gt;a different use case&lt;/strong&gt;. I use bookmarks for long-term things. They’re curated and I rarely open them until I need something to refer to. For example, I have a folder containing websites that provide quality SVG arts.&lt;/p&gt;

&lt;p&gt;When I leave something as a tab, they are either:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Things I use often - So I don’t see the point in closing them&lt;/li&gt;
&lt;li&gt;Things I want to continue researching&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  It’s part of a workflow
&lt;/h2&gt;

&lt;p&gt;Tabs that are left open go into a workflow that helps me sort out relevant pages. Naturally, I would consume the most relevant pages from the list. Those tabs are then closed in due time.&lt;/p&gt;

&lt;p&gt;Less relevant pages will be left in the list. Eventually, they automatically turn into an unloaded tab. And if they keep being not relevant, I will then close them.&lt;/p&gt;

&lt;p&gt;So perhaps I was extremely excited about the wonders of &lt;a href="https://www.deepsouthventures.com/i-sell-onions-on-the-internet/" rel="noopener noreferrer"&gt;Vidalia onions&lt;/a&gt; when I first saw them. But over time, the workflow proved that I don’t actually care about it all that much.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhak366popalv5hdylcdy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhak366popalv5hdylcdy.jpg"&gt;&lt;/a&gt;My tab workflow&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it out
&lt;/h2&gt;

&lt;p&gt;And that’s it! Now I can share this post with anyone that questions my workflow. While I’m not necessarily saying you should have that many tabs open, I would recommend you to try Sideberry if you’re on Firefox. On Chrome, your options would be to try Tree Style Tabs. Who knows it might change your life too.&lt;/p&gt;

&lt;p&gt;If you’re interested in this kind of content, &lt;a href="https://www.notion.so/Tech-929821000e564805bc0972cbc74604d8" rel="noopener noreferrer"&gt;follow me on Twitter&lt;/a&gt; where you’ll find similar things!&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>discuss</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Rust from 0 to 80% for JavaScript Developers</title>
      <dc:creator>Michael Salim</dc:creator>
      <pubDate>Sat, 30 Apr 2022 23:58:34 +0000</pubDate>
      <link>https://dev.to/michaelsalim/rust-from-0-to-80-for-javascript-developers-5159</link>
      <guid>https://dev.to/michaelsalim/rust-from-0-to-80-for-javascript-developers-5159</guid>
      <description>&lt;p&gt;This is a list of topic that will help you understand &lt;a href="https://www.rust-lang.org/" rel="noopener noreferrer"&gt;Rust&lt;/a&gt; quickly if you are a JavaScript developer. There are a lot of tutorial that start from scratch. But if you already know something else, why not compare them?&lt;/p&gt;

&lt;p&gt;These are differences that I wished I could refer to before starting Rust, kept short.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disclaimer!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I’m very far from being well versed in Rust. These are how I interpret them and no more than that. Rust’s documentation are vast so if you’re looking for details, please google them instead. &lt;a href="https://doc.rust-lang.org/book/title-page.html" rel="noopener noreferrer"&gt;The book is also a good starting point for learning rust&lt;/a&gt;. My goal is to list the important things so that you (and me in the future) can skip common programming concepts and focus on the differences based on the knowledge you already know.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types
&lt;/h2&gt;

&lt;p&gt;Rust is a typed language so it’s closer to TypeScript. You’ll have a much better experience if you already know TS.&lt;/p&gt;

&lt;p&gt;For the most part syntax are similar (&lt;strong&gt;variable_name: Type&lt;/strong&gt;) horray!&lt;/p&gt;

&lt;h2&gt;
  
  
  snake_case
&lt;/h2&gt;

&lt;p&gt;Yep, no getting around it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What’s this symbol?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Question mark (&lt;strong&gt;?&lt;/strong&gt;)
&lt;/h3&gt;

&lt;p&gt;You may see &lt;strong&gt;?&lt;/strong&gt; after a function call like so: &lt;code&gt;my_function()?;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;No, it’s not optional chaining. &lt;a href="https://stackoverflow.com/a/42921174/3101690" rel="noopener noreferrer"&gt;It’s an error handling magic for async functions&lt;/a&gt;. More about this later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://rust-lang.github.io/async-book/07_workarounds/02_err_in_async_blocks.html" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Exclamation mark on functions (&lt;strong&gt;!&lt;/strong&gt;)
&lt;/h3&gt;

&lt;p&gt;Example: &lt;code&gt;println!("{:?}", my_variable);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch19-06-macros.html" rel="noopener noreferrer"&gt;This indicates that it’s a macro&lt;/a&gt;. Basically a shortcut. Just use it if the function example shows it.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The &lt;strong&gt;&amp;amp;&lt;/strong&gt; symbol
&lt;/h3&gt;

&lt;p&gt;Example: &lt;code&gt;&amp;amp;your_variable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To get the reference. You’ll know this if you used low level languages like C. More later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Syntax
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Semicolon (&lt;strong&gt;;&lt;/strong&gt;) at the end of each line is mandatory&lt;/li&gt;
&lt;li&gt;Exception: Semicolon (&lt;strong&gt;;&lt;/strong&gt;) is not mandatory on the last line of a function. In this case, &lt;a href="https://doc.rust-lang.org/std/keyword.return.html" rel="noopener noreferrer"&gt;it’s a shortcut for returning that line&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://doc.rust-lang.org/book/ch03-03-how-functions-work.html" rel="noopener noreferrer"&gt;Function syntax is slightly different&lt;/a&gt;. Not a big deal.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="c1"&gt;// See no.2&lt;/span&gt;
  &lt;span class="c1"&gt;// or&lt;/span&gt;
  &lt;span class="c1"&gt;// return 3;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://doc.rust-lang.org/reference/attributes.html" rel="noopener noreferrer"&gt;Decorator syntax also different&lt;/a&gt;. It’s also called &lt;strong&gt;Attributes.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are these keywords?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  struct
&lt;/h3&gt;

&lt;p&gt;It’s a JSON object. (Ok maybe more complicated but see the docs for that)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Person&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&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;strong&gt;trait&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch10-02-traits.html?highlight=trait#traits-as-parameters" rel="noopener noreferrer"&gt;An interface&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  impl
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch10-02-traits.html" rel="noopener noreferrer"&gt;An implementation of trait&lt;/a&gt;. So class (?). &lt;em&gt;I’ve not used it&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  enum
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html?highlight=enum#enum-values" rel="noopener noreferrer"&gt;Quite similar to Typescript enums in a way&lt;/a&gt;. But you can store data in it. It’s pretty neat and quite an important concept to understand for async.&lt;/p&gt;

&lt;h2&gt;
  
  
  Console.log
&lt;/h2&gt;

&lt;p&gt;Not as easy unfortunately. &lt;a href="https://doc.rust-lang.org/std/fmt/index.html" rel="noopener noreferrer"&gt;More like &lt;strong&gt;printf&lt;/strong&gt; from other languages&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;println!("{:?}", my_variable);&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Library/Dependencies
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;Cargo.toml&lt;/code&gt; instead of &lt;code&gt;package.json&lt;/code&gt;. &lt;a href="https://github.com/rust-lang/cargo/issues/4" rel="noopener noreferrer"&gt;You’ll want to add them manually&lt;/a&gt; (instead of using a command like &lt;code&gt;yarn add&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;chrono&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.4"&lt;/span&gt;
&lt;span class="py"&gt;egg-mode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.16.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Importing
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch07-02-defining-modules-to-control-scope-and-privacy.html?highlight=module#defining-modules-to-control-scope-and-privacy" rel="noopener noreferrer"&gt;Rust has modules&lt;/a&gt;. It’s quite different from JS but basically:&lt;/p&gt;

&lt;p&gt;They’re sort of like namespaces. Here’s a breakdown on importing a dependency&lt;/p&gt;

&lt;p&gt;&lt;code&gt;use rocket::serde::{json::Json, Deserialize, Serialize};&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;use&lt;/code&gt;&lt;/strong&gt; - use this instead of &lt;code&gt;import&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;rocket&lt;/code&gt; - this is the package name&lt;/p&gt;

&lt;p&gt;&lt;code&gt;::&lt;/code&gt; - accessing a module&lt;/p&gt;

&lt;p&gt;&lt;code&gt;serde&lt;/code&gt; - the module name&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{json::Json, Deserialize, Serialize}&lt;/code&gt; - things to actually import&lt;/p&gt;

&lt;p&gt;Some more syntax:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;use chrono::prelude::*;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;use rusqlite::Result;&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Importing from local files
&lt;/h2&gt;

&lt;p&gt;Best explanation: &lt;a href="https://doc.rust-lang.org/rust-by-example/mod/split.html" rel="noopener noreferrer"&gt;https://doc.rust-lang.org/rust-by-example/mod/split.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;mod&lt;/code&gt; to the path/file you want to import to make the compiler include the module.&lt;/p&gt;

&lt;p&gt;Then &lt;code&gt;use&lt;/code&gt; to import it. Note: &lt;code&gt;mod&lt;/code&gt; automatically imports it too. In this case, you will need prefix it with &lt;code&gt;crate&lt;/code&gt;. &lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;your_file_or_module&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;mod.rs&lt;/code&gt; is a special filename which acts like &lt;code&gt;index.js&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;See the link above for examples.&lt;/p&gt;

&lt;h2&gt;
  
  
  Const vs let
&lt;/h2&gt;

&lt;p&gt;In JavaScript you’d use &lt;strong&gt;const&lt;/strong&gt; most of the time because it’s immutable.&lt;/p&gt;

&lt;p&gt;In Rust, you’ll want to use &lt;strong&gt;let&lt;/strong&gt; instead. This is immutable by default. If you want it to be mutable, use &lt;a href="https://doc.rust-lang.org/book/ch03-01-variables-and-mutability.html" rel="noopener noreferrer"&gt;mut&lt;/a&gt; keyword. &lt;strong&gt;const&lt;/strong&gt; are reserved for actual constants (so you can’t calculate the value from another variable)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;immutable_variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;mutable_variable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MY_CONSTANT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"CONSTANT"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Library Documentation
&lt;/h2&gt;

&lt;p&gt;If the Github repo doesn’t link to the documentation page, you can probably get to it like this:&lt;/p&gt;

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

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

&lt;h2&gt;
  
  
  Asynchronous
&lt;/h2&gt;

&lt;p&gt;By far, the 2 most confusing topic are &lt;strong&gt;futures&lt;/strong&gt; and &lt;strong&gt;ownership&lt;/strong&gt;. I would recommend reading a more comprehensive documentation for these. Let’s talk about futures first.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Future&lt;/code&gt; is like a &lt;code&gt;Promise&lt;/code&gt;. Unlike JS, Rust has a type for the result of the promise/future called &lt;code&gt;Result&lt;/code&gt;. It also accepts the error type on the generics (I wish JS has this).&lt;/p&gt;

&lt;h3&gt;
  
  
  Executing (or “consuming”) Future
&lt;/h3&gt;

&lt;p&gt;The standard library is quite barebones so you’ll need to import something else (Think &lt;a href="https://github.com/petkaantonov/bluebird" rel="noopener noreferrer"&gt;bluebird&lt;/a&gt; for JS). You need an executor to run a &lt;code&gt;future&lt;/code&gt;. I recommend using &lt;a href="https://github.com/tokio-rs/tokio" rel="noopener noreferrer"&gt;https://github.com/tokio-rs/tokio&lt;/a&gt; and reading their documentation.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;.await&lt;/code&gt; to await a function
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;async_function().await;&lt;/code&gt; Interesting syntax, yeah? Actually quite nice since you don’t have to wrap it with brackets like in JS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handling Result
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html" rel="noopener noreferrer"&gt;This is another important one&lt;/a&gt;. Rust is safe so you’ll need to handle everything. Yes, all error cases unlike JS!&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;Result&lt;/code&gt; enum has &lt;code&gt;Ok&lt;/code&gt; and &lt;code&gt;Err&lt;/code&gt;. If the future is successful, it returns &lt;code&gt;Ok&lt;/code&gt;, otherwise &lt;code&gt;Err&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Most comprehensive way to handle both cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello.txt"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above uses the &lt;a href="https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html" rel="noopener noreferrer"&gt;pattern matching syntax which is also great&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is quite verbose so there are 2 common ways to shorten it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using &lt;code&gt;.unwrap()&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example: &lt;code&gt;let my_value = async_function().await.unwrap();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This &lt;a href="https://stackoverflow.com/a/36362163/3101690" rel="noopener noreferrer"&gt;gets the success value and panics if Err&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Use this when you’re confident that it won’t error.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using the &lt;code&gt;?&lt;/code&gt; syntax&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This passes the error up. So your function needs to be able to return an error too (either a &lt;code&gt;Result&lt;/code&gt; or an &lt;code&gt;Option&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://stackoverflow.com/a/42921174/3101690" rel="noopener noreferrer"&gt;See this for example and its equivalent&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Ownership &amp;amp; References
&lt;/h2&gt;

&lt;p&gt;Heard of the borrow checker? Nothing much for me to say here. It’s the hardest thing in this list since it’s unique to rust. And if you’ve never handled references before, this topic might be a bit steep.&lt;/p&gt;

&lt;p&gt;Thankfully the rust book saves the day once again&lt;/p&gt;

&lt;p&gt;&lt;a href="https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html" rel="noopener noreferrer"&gt;Basically: Read part 4.1, 4.2 and 4.3&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  And that’s it!
&lt;/h2&gt;

&lt;p&gt;This list is actually shorter than I expected. I hope it’s useful for your journey.&lt;/p&gt;

&lt;p&gt;See a mistake or have any suggestions? Let me know!&lt;/p&gt;




&lt;p&gt;Like what you see here? Find &lt;a href="https://michaelsalim.co.uk/blog" rel="noopener noreferrer"&gt;more of my post&lt;/a&gt; or &lt;a href="https://recalllab.com" rel="noopener noreferrer"&gt;check my startup&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>rust</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Can you re-create this web component?</title>
      <dc:creator>Michael Salim</dc:creator>
      <pubDate>Tue, 18 Jan 2022 20:34:34 +0000</pubDate>
      <link>https://dev.to/michaelsalim/can-you-re-create-this-web-component-5a6l</link>
      <guid>https://dev.to/michaelsalim/can-you-re-create-this-web-component-5a6l</guid>
      <description>&lt;p&gt;&lt;strong&gt;Here's the challenge:&lt;/strong&gt; Re-create this pagination component from GitHub&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--d7jEbVMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2ARWV0B4K5Yw2wBJtJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d7jEbVMy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2ARWV0B4K5Yw2wBJtJ.png" alt="Pagination component of GitHub Issues" width="485" height="50"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some problems looks hard but turns out to be easy. Then you look at one that seem easy but it turns out to be a hard problem. This is one of the latter. It's definitely not an ordeal, but it probably took me a good hour when I originally estimated to finish this within a few minutes.&lt;/p&gt;

&lt;p&gt;Now, before you say &lt;em&gt;"This is easy! You're just a bad programmer"&lt;/em&gt;, &lt;strong&gt;I encourage you to try to re-create it!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://github.com/microsoft/vscode/issues"&gt;this page&lt;/a&gt; and scroll to the very bottom for reference. Make sure you test various scenarios. Here's some:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Go to the first page&lt;/li&gt;
&lt;li&gt; Last page&lt;/li&gt;
&lt;li&gt; In the middle of the page&lt;/li&gt;
&lt;li&gt; At page 2--6&lt;/li&gt;
&lt;li&gt; When there's not many pages (Eg: Total of 10 pages)&lt;/li&gt;
&lt;li&gt; On lower screen resolution (mobile)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Need more time? Don't worry I'll wait.&lt;/p&gt;

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

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

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

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

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

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

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

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

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

&lt;p&gt;Ok, let's explore the requirements. I hope this is detailed enough to make you wish you had these on &lt;em&gt;$DAY_JOB&lt;/em&gt;!&lt;/p&gt;

&lt;h3&gt;
  
  
  Exploring the Requirements
&lt;/h3&gt;

&lt;p&gt;There's 2 crucial points for this component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Able to navigate to first and last page&lt;/li&gt;
&lt;li&gt;  Able to navigate to the adjacent pages easily&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On its own, it's not a big deal. But once you account for responsive design, it's not as straightforward. So let's divide it per screen size.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Mobile
&lt;/h3&gt;

&lt;p&gt;When we don't have enough space to put anything, GitHub simply shows 2 buttons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--R5zUIoe1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2ACeplGQ5BCMgiWLRn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--R5zUIoe1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2ACeplGQ5BCMgiWLRn.png" alt="github_pagination_sm" width="195" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Simple, let's move on to the next size.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Tablet
&lt;/h3&gt;

&lt;p&gt;Here's where it starts to get tricky. When you're on the first page, show the first and the last page --- along with the "Previous" and "Next" button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Vf5iqzFd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2ALVik0p0FzlRUESOc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Vf5iqzFd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2ALVik0p0FzlRUESOc.png" alt="github_pagination_md_1" width="298" height="47"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But if we're on a different page then we'll want to indicate that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i44_MM4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AvVuGAoyFdD53asMK.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i44_MM4L--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AvVuGAoyFdD53asMK.png" alt="github_pagination_md_2" width="335" height="52"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What if we're somewhere on the middle? Notice the difference? GitHub doesn't show dots between 1 and 3 above. However, they show dots between 1 and 7.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hRX7Uuo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AOxpZTIFWAuW_sBrX.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hRX7Uuo---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AOxpZTIFWAuW_sBrX.png" alt="github_pagination_md_3" width="376" height="49"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Desktop
&lt;/h3&gt;

&lt;p&gt;Starting from the first page:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gtDB1UUb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AivZk7v38hUtOBBjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gtDB1UUb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AivZk7v38hUtOBBjq.png" alt="github_pagination_lg_1" width="502" height="46"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that the first five pages and the last two are shown.&lt;/p&gt;

&lt;p&gt;Let's try page 5:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cJBHUG27--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AGvhlawUwvxezQghv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cJBHUG27--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AGvhlawUwvxezQghv.png" alt="github_pagination_lg_2" width="560" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Page 1 to 7 is now shown. That makes sense because the 2 pages around our current page seems to be shown. Combining the fact that the first five pages are shown, this adds up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--n8vk8RVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AIavnv7_uiuzs4y2_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--n8vk8RVq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AIavnv7_uiuzs4y2_.png" alt="github_pagination_lg_3" width="598" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sure enough, we follow the same trend.&lt;/p&gt;

&lt;p&gt;Now, let's move to page 7.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--C2aWI32g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2A3XDMX7f9er9FktSJ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--C2aWI32g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2A3XDMX7f9er9FktSJ.png" alt="github_pagination_lg_4" width="604" height="44"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As expected, now we've lost page 3--4. Page 5--9 is shown because we're on page 7.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Some things not mentioned:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Previous and Next button becomes disabled when no page to paginate.&lt;/li&gt;
&lt;li&gt;  It needs to handle any number of pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's 2 additional cases to refer to:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ITY2mV8i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AOWijjclV5yvCoNJc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ITY2mV8i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AOWijjclV5yvCoNJc.png" alt="github_pagination_ex_1" width="281" height="50"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_uaSZBT7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AhNWlogGzAWq4uJW_.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_uaSZBT7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AhNWlogGzAWq4uJW_.png" alt="github_pagination_ex_2" width="579" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Didn't expect that many requirement did ya? Maybe you did --- I for sure didn't.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;So why did this took me much longer than expected to make?&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Unclear Scope
&lt;/h3&gt;

&lt;p&gt;If you tried to re-create this component without looking at the requirements I laid out, you might be surprised at how many cases there are to handle. Unclear scope is a big issue that leads to mismatch in development time and expectation. Most of you probably had an experience of being asked &lt;em&gt;"How long do you think this will take?"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And if you've experienced enough of those, you'll know to estimate a number and &lt;a href="https://erikbern.com/2019/04/15/why-software-projects-take-longer-than-you-think-a-statistical-model.html"&gt;multiply that by at least 1.5&lt;/a&gt;. So for a feature that you think might take a week, you'll say one and a half week --- Maybe even two!&lt;/p&gt;

&lt;p&gt;The more detailed a piece of work is, the better you'll be able to estimate. A vague task like &lt;em&gt;"Re-create X feature"&lt;/em&gt; is bound to go beyond your estimate no matter how simple it is.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The amount of different cases to handle
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;"Less is better"&lt;/em&gt; --- Somebody&lt;/p&gt;

&lt;p&gt;When I started on this component, my mindset was to create the component with as little parts as possible. Many developers like to shorten and use &lt;em&gt;"smart"&lt;/em&gt; ways to code as they get better at programming. This can be syntactic sugar, clever tricks or little known facts of a certain language. If you know &lt;a href="https://anssipiirainen.com/post/fp-tricks-for-simple-code/"&gt;Functional Programming&lt;/a&gt;, you know what I mean!&lt;/p&gt;

&lt;p&gt;At some point in my career, I did similar. I had fun figuring out how to write something clever. But that often increases the complexity of your code.&lt;/p&gt;

&lt;p&gt;Most importantly, &lt;code&gt;less !== better&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Simple is better. And less often times does not equal to simple.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4uS9vm_y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AfMhLMwpwPRs-7gav.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4uS9vm_y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1.medium.com/max/800/0%2AfMhLMwpwPRs-7gav.jpg" alt="If and loops" width="700" height="394"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://twitter.com/nice_byte/status/1466940940229046273"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some of you might be tempted to create a single elegant function to handle all the cases. In this example however, it might be much simpler and readable to handle each case on its own &lt;em&gt;if&lt;/em&gt; statement.&lt;/p&gt;

&lt;p&gt;Although if you manage to solve this problem elegantly, let me know through &lt;a href="//mailto:hi@michaelsalim.co.uk"&gt;e-mail&lt;/a&gt; or &lt;a href="https://twitter.com/IamMichaelSalim"&gt;twitter&lt;/a&gt;! I'd love to see it.&lt;/p&gt;

&lt;h3&gt;
  
  
  End result
&lt;/h3&gt;

&lt;p&gt;I originally set out to do this for &lt;a href="https://recalllab.com"&gt;Recall&lt;/a&gt;'s task explorer page. Here's how it looks in the end. Not too bad!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nAk1214Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/800/1%2AZvSrINphMFKzxdfIjNPSsQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nAk1214Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://cdn-images-1.medium.com/max/800/1%2AZvSrINphMFKzxdfIjNPSsQ.gif" alt="demo" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Final thought: This component might be a good test for a technical interview. What do you think?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at&lt;/em&gt; &lt;a href="https://michaelsalim.co.uk/blog/can-you-re-create-this/"&gt;&lt;em&gt;https://michaelsalim.co.uk&lt;/em&gt;&lt;/a&gt;&lt;em&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>challenge</category>
      <category>webdev</category>
      <category>programming</category>
      <category>ux</category>
    </item>
  </channel>
</rss>
