<?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: David  Morais</title>
    <description>The latest articles on DEV Community by David  Morais (@davidmorais).</description>
    <link>https://dev.to/davidmorais</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%2F149748%2Fbf10f0d5-7bfd-49f1-96e4-30d8fe20cc0b.png</url>
      <title>DEV Community: David  Morais</title>
      <link>https://dev.to/davidmorais</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davidmorais"/>
    <language>en</language>
    <item>
      <title>How Vibe Coding Changed my Development Workflow</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Fri, 28 Nov 2025 12:37:05 +0000</pubDate>
      <link>https://dev.to/davidmorais/how-vibe-coding-changed-my-development-workflow-38gb</link>
      <guid>https://dev.to/davidmorais/how-vibe-coding-changed-my-development-workflow-38gb</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;In this article, I walk through the process of building Phantom Pulse, highlighting how I used AI tools for planning and development. I share insights from exploring the LLM landscape, as well as the mistakes I made along the way. At the end of the article, I provide feedback and links to each tool I used, along with a brief explanation of my classification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build &lt;a href="https://phantom-pulse.netlify.app/" rel="noopener noreferrer"&gt;Phantom Pulse&lt;/a&gt; (with AI?)
&lt;/h2&gt;

&lt;p&gt;After being &lt;a href="https://davidmorais.com/en/blog/i-got-a-new-job" rel="noopener noreferrer"&gt;unexpectedly fired for the first time&lt;/a&gt;, I went to a remote location in Portugal's interior to relax for a bit and decide what I was going to do next. I was at a crossroads I had never been before—unemployed, with no immediate projects on my plate, and with no general sense of what lay ahead. Instead of dwelling on setbacks, I decided to make the most of my unexpected free time by experimenting with AI tools and exploring new workflows.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;#VibeCoding&lt;/code&gt;  was THE BUZZWORD during this time and, after all, my last two employers were becoming very AI-oriented, and while I was an early-access tester of GitHub Copilot, I never fully entrusted my workflow to our new AI overlords. So during this very offline vacation, I turned to Perplexity on my phone and asked it for a few project ideas using the Spotify API, which was something I had interest in working with in the past.&lt;/p&gt;

&lt;p&gt;I skimmed through a few of its suggestions and one of them piqued my interest: A music visualizer. I often have friends over and my TV acts as the speaker, however it usually only shows the album artwork of whatever is playing in the background. If I could make a visualizer that would respond to whatever was playing in the background, I could make these trippy colorful effects and add a bit of ambiance to my own living room. I was excited - a bit too much as it would eventually turn out - so I asked Perplexity how I would approach building this and the approach made sense to me, so I asked it to break the project into tasks that I would then put into Linear. This spontaneous experiment marked the beginning of a fascinating journey into AI-driven development, blending planning, coding, and automation in ways I hadn't fully imagined before.&lt;/p&gt;

&lt;p&gt;Armed with what seemed to be a doable project, I asked Perplexity for some branding elements like a logo and a color palette, and I was set on starting to work on it as soon as I grabbed a computer.&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%2F0pukf42dcdig43kbvz3e.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%2F0pukf42dcdig43kbvz3e.png" alt="The conversation with Perplexity" width="679" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When hallucinations meet constraints
&lt;/h3&gt;

&lt;p&gt;I got back home and I pulled the project conversation from Perplexity and started adding issues to Linear. Surely there must be a way to do this from an LLM, I thought. With the Linear MCP installed, I tried pasting the tasks to GitHub Copilot and to Cursor and hoped one of them would then automatically add the tasks to Linear. They were not able to complete the task—judging from the error messages—because Linear's MCP couldn't match my emoji-prefixed project title when I omitted the emoji in the prompt.&lt;/p&gt;

&lt;p&gt;So I added all tasks manually to Linear, which did not take me a long time, but as a developer, alt+tabbing and copy/pasting over and over just feels dirty.&lt;/p&gt;

&lt;p&gt;I remember Google I/O had just been around that time and they had released &lt;a href="https://stitch.withgoogle.com/" rel="noopener noreferrer"&gt;Stitch&lt;/a&gt; recently, so I gave it a prompt of what I wanted to build and it provided me with a nice-looking prototype, thus avoiding any kind of “dev design” one so often has to do.&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%2Fh61pon20v8xqvzlqe5u3.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%2Fh61pon20v8xqvzlqe5u3.png" alt="The prototype provided by Google Stitch" width="800" height="625"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I started working at it: I set up the project with Vite, shadcn/ui, Tailwind, and Biome. I built most of the UI components and added all the contexts I would need for future state management. I implemented Spotify authentication with Supabase and, at the end of a few weeks, I had what can be described as a basic music player: I could listen to music, see what was being played, and pause/stop tracks. Time to connect what was being played to my React UI—and this is where it goes south.&lt;/p&gt;

&lt;p&gt;When I was planning for this project—or rather, when I asked Perplexity to plan this project—I was not concerned at all with the visualizer reacting to whatever was being played in the background, as I had already heard of the &lt;a href="https://developer.spotify.com/documentation/web-api/reference/get-audio-features" rel="noopener noreferrer"&gt;audio-features API&lt;/a&gt;. I was planning on using it to pass some initial parameters to the visualizer, such as track tempo and energy. It turns out this endpoint had been deprecated in late 2024 and Spotify was not allowing any new applications to use it. I assume this was due to the increase of developers using it to train LLMs and other AI-related shenanigans. Despite the fact that there was an active marketplace scene selling API keys for apps with access to this endpoint, I tried another approach.&lt;/p&gt;

&lt;p&gt;I tried to use a Web API for screen sharing that would allow me to share only audio; however, there was support for this only in Firefox. Before giving up on the idea of a Spotify Music Visualizer, I also considered making a desktop client (with Electron), but I would have to mess with OS-specific libraries to have it pick up any sound from the user's desktop.&lt;/p&gt;

&lt;p&gt;In each of these approaches, I would turn to Perplexity or Gemini, which correctly told me that none of them would work. Still, I had been fooled before, so I verified my findings the old-fashioned way (Stack Overflow &amp;amp; GitHub comments) and decided to shift to something else—and this is how Phantom Pulse became a music quiz game. I did, however, use &lt;a href="https://www.warp.dev/" rel="noopener noreferrer"&gt;Warp&lt;/a&gt; terminal's agent mode and &lt;a href="https://geminicli.com/" rel="noopener noreferrer"&gt;Gemini CLI&lt;/a&gt;, which did okay. Warp is my main terminal and I use it across all platforms; its recently launched Code editor is awesome for short edits, but as a non-CLI user, using these felt a bit clunky, especially Gemini CLI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Implementation: Tools that powered the process.
&lt;/h3&gt;

&lt;p&gt;So for actually building the thing I went out to experiment and, like all the cool kids, VIBE CODE!&lt;/p&gt;

&lt;p&gt;The concept still feels wildly wrong for me, so I did not want to turn 100% to AI tools. Like I wrote above, the whole setup was made by me. I could have provided a screenshot of the prototype Stitch spat out, but I didn't. I built the skeleton and the router that would power the app.&lt;/p&gt;

&lt;p&gt;I started by using Cursor when I wanted to create some React contexts for relevant parts of the application. I was not surprised by Cursor practically one-shotting this—after all, there’s plenty of documentation and examples out there on how to implement these. However, when I asked it to build a custom wrapper with &lt;code&gt;react-query&lt;/code&gt; for my Spotify music player and authentication, it started providing code that simply would not work and was so bloated that I stopped using it altogether. ⚠️ For reference, I was using the free plan.&lt;/p&gt;

&lt;p&gt;It was around this time that I received an email with an invite to the early access of &lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;. I installed it on my Arch-based machine and, to my surprise, the desktop icon was showing correctly (unlike Cursor 🙃). I read through their website to see exactly what it was capable of and decided to put it to the test by asking it to build the Playlist Selection component. This component would fetch the user's playlists from the API or &lt;code&gt;react-query&lt;/code&gt;'s cache. So before doing anything, it wrote three documents: a &lt;code&gt;product&lt;/code&gt; definition, a &lt;code&gt;tech&lt;/code&gt; document with the stack and basic commands, and a &lt;code&gt;structure&lt;/code&gt; file that provided guidelines on where to store new files.&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%2Ffgh4s7pwkrrk7tqj34ks.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%2Ffgh4s7pwkrrk7tqj34ks.png" alt="Kiro's planning abilities" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After Kiro understood everything about my project, including product details and tech specifications, it went to town on the feature I had just assigned it. Again, before starting any code, Kiro wrote a definition for the feature and asked me to validate multiple aspects of it. My friends, I felt like a PM and wondered if this is what they actually do with their time. As for the feature itself, it worked very nicely and, apart from a few tweaks and improvements, it did the job wonderfully, with the added value of having created some actual documentation for the app.&lt;/p&gt;

&lt;p&gt;Because I was on the Early Access, I had a ton of tokens, and to my surprise, this ordeal had only burnt 30% of my tokens. However, this was still far more than the free plan and, given my current status, I decided not to commit to the Pro plan (like Cursor, priced at 20 USD/month).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Power of Copilot Agents
&lt;/h3&gt;

&lt;p&gt;It was at this point that I turned to &lt;a href="https://github.com/features/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; again, as it was a familiar tool that I had been using ever since it came out. I sometimes tell the story of having it activated during a morning at work without knowing (early access would roll out to random users) and feeling like it was magic. I know how powerful it is, having used it before to create entire test suites, and I had been following the news and updates in the space to know that it is one of the best tools out there. The decision was made easier by the fact that the Pro plan was half of the other options out there. So I started my free trial with the intention of keeping the subscription afterwards.&lt;/p&gt;

&lt;p&gt;Knowing Copilot, I am also aware of its limitations, so my usage of it consisted mainly of small tasks and code improvements while I did most of the heavy lifting—and it did pretty well at it. However, eventually, I found out about &lt;a href="https://github.com/features/copilot/agents" rel="noopener noreferrer"&gt;Copilot Agents&lt;/a&gt; and this was the game-changer. Agents allow me, from GitHub's dashboard (or now from the VSCode sidebar), to specify what I want built while I follow the process, which eventually culminates in a PR.&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%2F5sj8d77v4ik027hxsnqk.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%2F5sj8d77v4ik027hxsnqk.png" alt="GitHub Copilot Agents are a game changer" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This effectively meant that I could work on something else while delegating other tasks to Copilot.&lt;/strong&gt; Whereas up until this point I would stop doing whatever I was doing and let the AI tool take over, this tool just does its own thing while I do mine. &lt;strong&gt;Copilot had become my junior developer.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The new workflow - #copilot🤖
&lt;/h3&gt;

&lt;p&gt;So for the next round of tasks, I decided to get back to the once-lost #VIBECODING spirit a little bit and when planning what I was going to tackle from Linear, I decided that I would delegate a few issues that I considered easy-but-boring to Copilot, and thus, the #copilot🤖 label was born.&lt;/p&gt;

&lt;p&gt;Copilot did extremely well in these small tasks and bugs, sometimes even providing a screenshot when opening up a PR. It did show a little difficulty with Supabase-related tasks because I hadn't set up any environment variables or a mock environment for working on authentication-related issues.&lt;/p&gt;

&lt;p&gt;I found myself doing actual code reviews and asking for changes from Copilot. Sometimes, if the change was too mundane, I would check out the branch and make the change myself in hopes of saving a few tokens, but I felt I could rely on Copilot to do most of what I asked.&lt;/p&gt;

&lt;p&gt;One thing I found somewhat off-putting was that, despite the instructions specified in my &lt;code&gt;AGENTS.md&lt;/code&gt; file, Copilot would generate tests even when I didn't request them. It would also sometimes add dependencies to some &lt;code&gt;useEffect&lt;/code&gt; instances to satisfy the linter—despite having an &lt;code&gt;ignore&lt;/code&gt; comment with an explanation above—which would lead to infinite loops. This behavior went away after I instructed it to ignore the exhaustive-deps linter warning. However, I still found myself checking out branches and running development servers to check if everything was working fine, making some changes, and merging Copilot's branch with &lt;code&gt;master&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before rating the tools and concluding this article, I think it is important to highlight how integrating these AI tools fundamentally changed our approach to software development. My little experiment showed that if you combine human creativity, planning, and care, you can speed up the workflow, reduce boring and repetitive tasks, and improve the final product. This synergy between human and machine not only optimized my workflows but also allowed me to be more creative and focus on the product.&lt;/p&gt;

&lt;h4&gt;
  
  
  Tools Used
&lt;/h4&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://www.perplexity.ai/" rel="noopener noreferrer"&gt;Perplexity&lt;/a&gt; - 3/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: For search and planning, and for creating branding elements like logo, name, and color palette.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the end, it either hallucinated browser capabilities or assumed I was using a deprecated API endpoint, forcing me to shift to a whole different end product. I'll take some blame for this, as I could not verify the information I was provided at the time and I did not double-check it when creating the Linear tasks. However, for searching the web for answers it is a great alternative to other tools like ChatGPT, Gemini, or even Google.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://linear.app/docs/mcp" rel="noopener noreferrer"&gt;Linear MCP&lt;/a&gt; - 1/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: Tried to create project issues from a prompt.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Linear's MCP could not bulk-create the tasks from Perplexity, I'm assuming because it could not identify my emoji-titled projects from the prompts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://stitch.withgoogle.com/" rel="noopener noreferrer"&gt;Stitch&lt;/a&gt; - 3/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: Prototyping the application.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While it did a good job with the prototype, I needed to repeat myself sometimes when prompting it. Also, the lack of any UI (at the time) to delete unused screens made the whole prototype very bloated.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://www.warp.dev/" rel="noopener noreferrer"&gt;Warp Terminal&lt;/a&gt; - 4/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: Main terminal + Warp Agent&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It is an awesome terminal for those who prefer GUI-oriented software. It's got free, built-in AI features that I use daily as fast alternatives to looking up commands and system locations in Google, but for developing features it felt like it was not there yet. Plus, the paid plans are a bit too much for what a terminal tool should cost, in my opinion.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://geminicli.com/" rel="noopener noreferrer"&gt;Gemini CLI&lt;/a&gt; - 3/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: CLI-based assistant&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It worked fine for quick experiments, but as a non-CLI user it felt clunky compared to editor-integrated tools.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://cursor.com/" rel="noopener noreferrer"&gt;Cursor&lt;/a&gt; - 3/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: Developing&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It took too many attempts to build basic features for the price. Also, the Arch-based build does not display the desktop icons correctly. I know their recent update features an Agents UI and it's an awesome tool, but for now I will stick with Copilot.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://github.com/copilot" rel="noopener noreferrer"&gt;GitHub Copilot&lt;/a&gt; - 5/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: Developing&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;It's effectively the first of its kind and feeds on the world’s largest codebase. The Agents feature is a game-changer. Despite the fact that other tools are starting to have this feature too, this one seems the most cost-effective.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  &lt;a href="https://kiro.dev/" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt; - 4/5
&lt;/h5&gt;

&lt;p&gt;&lt;strong&gt;Usage: Planning &amp;amp; Developing&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I only had a sneak peek of Kiro before I switched to test another tool, but I loved how it provided me with the entire app documentation and asked me a bunch of questions about the feature it was implementing. This feature in particular was a bit complex and Kiro delivered it with almost no issues.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Conclusions
&lt;/h3&gt;

&lt;p&gt;In reflecting on the journey of building Phantom Pulse, it’s clear that the integration of AI tools has fundamentally transformed the way I approach software development. From initial ideation—fueled by AI-driven planning and brainstorming—to designing prototypes and automating repetitive tasks, each step was enhanced by the assistance of advanced tools like Kiro, Copilot, and others. These tools didn't replace my creativity or decision-making—they amplified them, allowing for faster iteration, better documentation, and even a touch of fun during what could have been tedious workflows.&lt;/p&gt;

&lt;p&gt;However, this journey also highlighted the importance of human oversight. While AI can streamline tasks and generate impressive outputs, it’s essential to verify, adapt, and iterate on its suggestions to ensure alignment with your vision. Mistakes like encountering deprecated APIs or hallucinations serve as reminders that these tools are aids, not infallible sources of truth.&lt;/p&gt;

&lt;p&gt;Ultimately, the Phantom Pulse project exemplifies a new era in development—one where human ingenuity is complemented seamlessly by AI collaboration. As these tools continue to evolve, expect even more streamlined, creative, and efficient workflows that empower developers to focus on what truly matters: building innovative, meaningful technology. The future of development is here, and it’s deeply human plus totally AI-enabled.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>programming</category>
    </item>
    <item>
      <title>I got a new job - and I was fired after 45 days</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Mon, 10 Nov 2025 12:00:37 +0000</pubDate>
      <link>https://dev.to/davidmorais/i-got-a-new-job-and-i-was-fired-after-45-days-7fc</link>
      <guid>https://dev.to/davidmorais/i-got-a-new-job-and-i-was-fired-after-45-days-7fc</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;You can find these and more articles in &lt;a href="https://davidmorais.com" rel="noopener noreferrer"&gt;my website&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Back in May of 2025, I quit my job of 3 and a half years to pursue new challenges. I had landed a new role and was genuinely excited about it. However, just 45 days into the job, I was unexpectedly let go. This article shares my experience and reflections on that period, and most importantly, what I learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The (New) Beginning 🌱
&lt;/h2&gt;

&lt;p&gt;The decision to leave my previous job was actually a combination of two very important factors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Back in 2024 the company was scaling up and adopted new coding practices that did not align with what was discussed during my hiring process, which at the time was a deal breaker for me. I understand that as the company grew, changes were inevitable, but I felt that the new direction was not in line with my professional values and goals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When these changes were announced by the higher ups I went to a handful of interviews, but ultimately decided to stay as I had a good relationship with my team and manager. In the end, I ended up learning a lot from staying and adopting some of the new practices.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;After a year of working under the new practices, I felt that my growth was stagnating. And the feeling was mutual; I was placed under a &lt;strong&gt;Performance Improvement Program&lt;/strong&gt; (PIP), and on the same day I started working on personal projects and a few articles in order to resurrecting my LinkedIn profile from under the dust bin of the algorithm.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As we proceeded with the PIP, I got to a point which I realized that no matter what I did, the outcome would not favor me. I would not go as far as to say that they were trying to push me out, but on one moment I was receiving feedback to be more communicative and in the other, I was being bombarded on Slack that I was over-sharing stuff with the team. It was a confusing time to say the least.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;So not long after I started re-surfacing on LinkedIn and other platforms, I got a message from this recruiter, let's call him H, who had found my profile and was interested in discussing a new opportunity. H was from a company that was working on the productivity space, something I quite enjoy. I had actually used the product from H's company before. I underwent the interview process, which was fairly standard with a take-home exercise, a technical interview, and a cultural fit interview. In the end, I got the offer and decided to take it.&lt;/p&gt;

&lt;p&gt;I remember my biggest motivations were the fact that I would be working with a product I would use myself - as opposed to the localized accounting software I was working at the time. I had grown saturated of building tables or lists and sidepanels over and over again for the past 3 and a half years. So the fact that the product had been around for a while and the fact that they mentioned wanting to improve the codebase and developer experience, had me genuinely excited over what was ahead for me.&lt;/p&gt;

&lt;p&gt;So I gave my notice at my previous job, and started preparing for the new role.&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%2Fzskw8fnwjreel2ex2gxy.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%2Fzskw8fnwjreel2ex2gxy.png" alt="David decides to join a new job in Austria" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Onboarding ✈️
&lt;/h2&gt;

&lt;p&gt;In June, I headed out to Vienna to meet the team and the company and although I was received warmly by everyone, I also started to spot some early signs of what would become some serious red flags. I do not want to write anything that would get me in trouble from a legal point of view, so I will just say that there some decisions that were taken that &lt;strong&gt;clearly&lt;/strong&gt; did not reflect what it was presented. There were two main things I observed that I consider to be the biggest red flags:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The codebase relied heavily on a custom built solution for, well, everything. This alone was not a red flag, I mean, this could be something really powerful that made obsolete some really powerful libraries that are used for any project - I'm talking about state management, style preprocessing, type safety and schema validation, that thing had it all - it  seemed really useful. However, the feedback I got from 99% of the devs I interacted to, was that we were dealing with this legacy solution everywhere and everything was entangled in it. This made it so that they had been building on top of it. Something that everyone acknowledged was getting in the way of the developer experience at the company, but also everyone was building their own stuff on top of it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The second red flag came in the form of Google Drive access and some pretty heavy to digest previous presentations that reflected the company's growth had stagnated for a &lt;strong&gt;LONG&lt;/strong&gt; time.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Still, I tried to be optimistic for a change and actually thought that I had arrived to enact some much needed change and ideas. The feedback I got from everyone is that the company itself was pivoting and looking to bring in some fresh ideas to the table. And I came back home with the idea that in the end, this was going to work out perfectly.&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%2Fhf7v9z24y65908c9xqv9.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%2Fhf7v9z24y65908c9xqv9.png" alt="David in Wien" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Tenure In A Nutshell 🚀
&lt;/h2&gt;

&lt;p&gt;I remember the setup process being really painful; there were no clear guidelines, and the instructions were either outdated or plainly wrong. The concept of having &lt;code&gt;README.md&lt;/code&gt; in their repos was close to non-existent 🚨. So my first PR was precisely updating a bit the old instructions and the second was adding a &lt;code&gt;README.md&lt;/code&gt; to the repo.&lt;/p&gt;

&lt;p&gt;I remember that my onboarding came a bit at a bad time since my onboarding week had a lot of people away on vacation and my second week was the company retreat, so I was basically alone, except for one or two colleagues who would reply to my enquiries at least once a day. Additionally, most of my squad mates were backend focused, so they were not very familiar with how the front end setup was done.&lt;/p&gt;

&lt;p&gt;My manager was actually a beacon of hope in the middle of everything. He was pretty knowledgeable about the product, he knew what everyone in the squad was doing and he knew the blockers each of us were facing, and most importantly, he knew the hardships the company and the product were facing and the poor developer experience repo he was onboarding me onto. Still, I felt motivated each time I spoke with my manager regarding the hardships I was facing and the improvements I saw that could be made, and I was always incentivized to open a PR or to open a discussion with the relevant people on Slack.&lt;/p&gt;

&lt;p&gt;My first tasks were simple enough: Fix a copy issue here, remove a button there, update a few links to the cookies to the new domain. And I was tackling these simple issues with a certain degree of respect that I normally would not: I was writing tests for components that only displayed links - because they had no unit tests and no framework for developers to write them. Each PR had an extensive description, copy-paste commands to run the newly-added specs and screenshots of the changes - I had picked this habit from my Pennylane tenure, and again, I was trying to show a bit of what we could be doing, opposed to PRs with only a title and lacking any description. The feedback I was getting from my manager was positive and, I cannot stress this enough, I was motivated. And I felt like I was being a part of a positive change in the air.&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%2Frxo98o9op1b4z05g955u.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%2Frxo98o9op1b4z05g955u.png" alt="Life was good in Wien" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  My First Big Issue 🐞
&lt;/h3&gt;

&lt;p&gt;During my onboarding week I was presented with an issue the application had for quite some time and that was about to breach some SLAs if not fixed. This was an extremely edge case issue that broke a core part of the application for some select users: drag and drop. There were hundreds of customer service tickets for this (not so  many from the tech side), however it was an issue that no one in the company had managed to reproduce. There was company lore that said a colleague started working on this before taking a medical leave, and I did find a PR where a colleague did start to replace all drag and drop functionality. After digging and asking a series of questions around I learned that some months ago they replaced their bundling engine and after doing so they started receiving reports of users with a broken scroll. So they attempted a fix and while the scroll was indeed fixed for normal interactions, however when you interacted with an element, for example, a card, you lost all scroll capabilities when dragging it. This was already something a bit edge-casy, now add that to the fact that only certain users, in remote desktop, were reporting the issue, and again, &lt;strong&gt;no one in the company had manage to reproduce at that point&lt;/strong&gt; and it was &lt;strong&gt;poorly documented.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TLDR 👉️ &lt;em&gt;old, edge case issue that no one could reproduce. i had to investigate and dig around&lt;/em&gt; &lt;strong&gt;&lt;em&gt;a lot&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;to learn the actual issue and come up with a plan.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I wrote a battle plan, shared it with my superiors who eventually gave me the green light to go ahead with it. Before trying anything, it took me some time, but I finally had a way to reproduce the issue with an old company computer !&lt;/p&gt;

&lt;p&gt;My first approach was to go on the engine - that everyone was so adamant was the reason for this - and add any polyfills that may have been missing. This was a fast solution. If it worked, great. If not, we weren't wasting much time with it. It did not work. So I remember drafting a PR with a few improvements and dependency updates and I got back to my battle plan.&lt;/p&gt;

&lt;p&gt;Now I thought about doing what my colleague started, but I did not want to go as far as replacing this whole core functionality, so I tried creating a sandboxed context for the drag and drop features that required scroll, but that was not good because of the existing outer context which relied heavily on that old custom-built-solution-for-everything.&lt;/p&gt;

&lt;p&gt;So I ended up doing a &lt;em&gt;frankenstein&lt;/em&gt; of sorts and managed to use new components for the UI and their librarie's events for the rest. I'd guess a monster with about 75% of my code and the 25% using their library with a sanitized wrapper.&lt;/p&gt;

&lt;p&gt;TLDR 👉️&lt;em&gt;Tried some quick fixes, didn’t work, so I mixed my own code with bits of the old library—it’s a Frankenstein solution, but it works!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2roda6y74q4d8gem12cj.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%2F2roda6y74q4d8gem12cj.png" alt="David fixing bugs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the end, I managed to fix the issue that was caused by their fix and the original issue too. My manager was happy, my manager's manager was happy. And the plan was to actually replace the remaining features with this solution and implement it app-wide. We just needed to work out some quirks, here and there, and we would proceed.&lt;/p&gt;

&lt;p&gt;I remember already having a POC (&lt;em&gt;proof of concept&lt;/em&gt;) on Monday and my manager telling me to present a battle plan to the team, like the last time, before starting to write code, and I agreed. I was already writing a document, just needed some time to polish it and present it to the team for any questions or clarifications there may be. So that's what I did.&lt;/p&gt;

&lt;p&gt;At the end of the day the document was all polished up and I had this somewhat implemented in one of the features that had my new solution. I do not remember how presentable the code was, nor do I recall if I ended up posting my plan in Slack.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Next Morning 🌄
&lt;/h2&gt;

&lt;p&gt;It was about 9h30 when I woke up the next morning - I had no meetings, my work was ahead of schedule and I had received some great feedback the day before. So I look at the phone and I remember an onboarding session I had with the security department in which they demo'd a few phishing emails. I thought I had just received one of those. Still, it said "Termination Notice", I had to open it. So I did, and I quickly confirmed this was indeed from the company's domain. And I jumped out of the room to grab my computed and check Slack. What the hell was happening ? Everything was going good, everyone was happy with my work. My head was filled with questions at this point, but I still was not entirely certain this was happening. How could it ? This was probably a mistake.&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%2F3g6v8q69sakct038ih35.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%2F3g6v8q69sakct038ih35.png" alt="David learns he was fired" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I reached out to my manager as soon as Slack loaded. He was at an appointment so did not fully know what was going on, but I learned two very serious things from him: There was a company-wide announcement and he had also received an email similar to mine. This is the first time it settled in for me that this was real. I had been fired. "Not for performance reasons," the email said, which made it a tougher pill to swallow. I read the email again. It had my name, it had a few 'custom' words that seemed specifically aimed at me, such as "our short time together".&lt;/p&gt;

&lt;p&gt;I started digging through Slack's channels and quickly came across a message from the CEO, whom I never met, summoning everyone for an all-hands meeting with a 15-30 minutes notice. The meeting would start at 8:00 for my timezone. Nothing else. Around this time a portuguese colleague reached out asking if I had been affected by the 'restructuring'. I had. But I still had no idea what was going on, so I directly replied to the CEO's message and checked the "Send to #general" box and asked if someone could have the decency to explain what had happened, for those who may be on a different time zone, at the doctors or in vacation (it was July), to know exactly was going on.&lt;/p&gt;

&lt;p&gt;Before I got a reply on that, my colleague explained that 30% of the company had been fired. Just like that. I asked for a few names I retained during my short time there, and most of them were gone. In the other channel, I got a reply from some HR person that also confirmed this scenario, and a few minutes after this, the CEO himself apologized for how the news was given and a bunch of other stuff that I could not care less and explained the situation. This was two hours after calling a company-wide meeting to lay off 30% of the workforce. The email was sent 15 minutes after the meeting started.&lt;/p&gt;

&lt;p&gt;In the end I received a not-so-big compensation package, on the account of being in my experimental period, and just had to come to terms that for the first time in my life, I had been fired. And I have been avoiding discussing this publicly, maybe feeling a bit of that &lt;em&gt;sweet imposter syndrome.&lt;/em&gt; Still life goes on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned From All Of This ? 🤓
&lt;/h2&gt;

&lt;p&gt;While I had a while to digest this and have no ill-will towards the company, I like to think that as a developer, I learn &lt;strong&gt;A LOT&lt;/strong&gt;, whenever I switch jobs. In this case, the learnings were not much on the technical side of things, however, here are some takeaways from this experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;RED FLAGS MATTER 🚨&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I will be paying alot more attention to what I consider to be red flags - poor documentation, inconsistent information, &lt;strong&gt;legacy systems no one wants to maintain&lt;/strong&gt; , and most importantly, hints at &lt;strong&gt;stagnation in company growth&lt;/strong&gt;.  I saw and validated these with colleagues and they were indeed meaningful signals.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;CULTURE FIT AND LEADERSHIP TRANSPARENCY 🪞&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Companies are always excited to welcome everyone. It's always a big moment of joy and celebration, like if the CEO had birthed himself another team of employees. This was the first company that I joined that the CEO was not a founder and  that he did not bother to introduce himself during my time in Vienna. I did not think much of it at the time, but this lack of tact also helps explain how the whole layoff was handled.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;STABILITY IS NOT GUARANTEED - EVEN FOR GOOD PERFORMERS 🏓&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I am not over myself when I say that everyone was happy with my performance. I know I'm not being arrogant when I say that I was having a positive impact on the product. I know this because I was told, repeatedly by my manager, my team-mates, my manager's manager and even on the termination email. And sadly, that does not equal stability, as I learned. It's not personal either. &lt;strong&gt;I reckon that in a cost-saving scenario it's easier to fire those in the experimental period.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;ALWAYS HAVE A BACKUP PLAN 🤫&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I think this was also one of my biggest mistakes. I saw what a shit-show the code was, I saw the status of the company growth, I saw how much everyone had stopped trying to improve the codebase. If I had a backup, I probably would jump out after Vienna. If I had a backup lined up, I would probably have just say my goodbyes and start working full time in the next month. I did not.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;GROWTH IS NOT LINEAR 📈&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I was told this on my second job, right after I managed to get a pay rise with the agency. My career will include setbacks, unexpected turns and periods of uncertainty. I am grateful enough to have had eight great years of growth and continuous employment. I do not expect that my next job will be better than what I had, both in terms of salary and in terms of tech stack.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;So, here’s the short version: I quit my comfy old job, jumped into a shiny new one, and—plot twist—got laid off just 45 days later. (Turns out, “trial period” isn’t just for apps!) The red flags started waving early: the codebase was basically a spaghetti monster nobody wanted to touch, documentation was a unicorn, and the company vibe felt... well, stale. But I powered through, fixed a legendary bug, even wrote real documentation (gasp!), and got a bunch of kudos—right before getting the boot with a “not your fault” email. Classic.&lt;/p&gt;

&lt;p&gt;What did I learn? Trust your gut when something smells fishy; company culture isn’t always as advertised; sometimes even doing a great job won’t save you from a company “restructuring”; and for the love of all that is caffeinated, always have a backup plan. Oh, and growth is messy—sometimes it looks a lot like falling on your face. But hey, at least there’s a good story (and some fresh LinkedIn content) at the end of it.&lt;/p&gt;

&lt;p&gt;You can read these and more posts in &lt;a href="https://davidmorais.com" rel="noopener noreferrer"&gt;my website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>career</category>
      <category>leadership</category>
      <category>hiring</category>
    </item>
    <item>
      <title>My top (lesser known) VS Code extensions</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Mon, 22 Sep 2025 08:00:00 +0000</pubDate>
      <link>https://dev.to/davidmorais/my-top-lesser-known-vs-code-extensions-1f8e</link>
      <guid>https://dev.to/davidmorais/my-top-lesser-known-vs-code-extensions-1f8e</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Are you tired of “best VS Code extensions” lists recycling the same handful of crowd favorites? Me too. That’s why I’ve dug through my own collection and surfaced some underrated gems that have genuinely improved my day-to-day coding workflow. In this post, you’ll find a curated selection of lesser-known (but highly useful) VS Code extensions—each with a quick explanation of what they do and why I keep them around. Whether you’re looking for ways to streamline navigation, personalize your setup, or just add a bit of fun to your coding environment, there’s probably something here you haven’t seen before.&lt;/p&gt;

&lt;p&gt;Before I jump in, a quick note: none of these extensions are sponsored. They’re simply tools that I’ve incorporated into my own workflow and found genuinely helpful. I’ll provide a brief description of each, along with a few thoughts on why they stand out for me.&lt;/p&gt;

&lt;p&gt;Hopefully, you'll discover something that sparks your interest or addresses a pain point you didn’t even realize you had. With that out of the way, let’s dive into the list.&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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Finstalledextensions.png%26w%3D3840%26q%3D75" 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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Finstalledextensions.png%26w%3D3840%26q%3D75" alt="A list of my installed extensions" width="586" height="1020"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=tylerxwright.vscode-line-surfer" rel="noopener noreferrer"&gt;Line Surfer 🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Never lose track of your curent line of code&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First thing anyone reading this list will understand is that I like to test how many icons, emojis and colors I can put in my VS Code setup without losing too much focus 😓 Line Surfer helps with both these. The GIF below illustrates exactly what it does.&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%2Fgithub.com%2Ftylerxwright%2Fvscode-line-surfer%2Fraw%2FHEAD%2Fresources%2Fline-surfer-demo.gif" 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%2Fgithub.com%2Ftylerxwright%2Fvscode-line-surfer%2Fraw%2FHEAD%2Fresources%2Fline-surfer-demo.gif" alt="Untitled" width="8" height="5"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So I basically have my configured colors (⚠️ with opacity) and whenever I lose track of my cursor, I can easily identify where it is due to what LineSurfer does. It highlights your focused line and a few other above/below it. All of this is configurable.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-squarl" rel="noopener noreferrer"&gt;Squarl Bookmarks 🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;A bookmark manager for your codebase&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This powerful bookmark extension makes it easy to save files within large codebases, and is especially useful even when dealing with files that have identical names or complex, confusing paths. You can have your own personal bookmarks or you can store a configuration in &lt;code&gt;.vscode/settings.json&lt;/code&gt;, where you can include essential bookmarks and helpful links to &lt;strong&gt;simplify onboarding for new team members&lt;/strong&gt;. The extension also lets you organize your bookmarks into groups or categories for easier navigation and management.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=bruceyuhb.hsl-preview" rel="noopener noreferrer"&gt;HSL Color Preview 🔗&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Preview hsl colors in your editor&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This one is pretty straightforward in its purpose. I don't exactly remember why or when, but clearly I needed to see which how an hsl color looked like in VS Code and this extension does exactly that.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=rioukkevin.vscode-git-commit" rel="noopener noreferrer"&gt;VSCode Git Commit Message 🔗&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Add a set of emojis and prefix your commit titles with them&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is by far the extension I’ve had installed the longest. As mentioned earlier, I’m a big fan of (over)using emojis. So when I discovered the gitmoji and emoji log trend, I started using that standard for about 90% of my personal commits.&lt;/p&gt;

&lt;p&gt;However, I prefer to customize my setup, and the existing extensions usually follow a strict standard, often tailored to the developer’s preferences. So I wanted an extension that not only allowed me to use whatever set of emojis I wanted, I also wanted to be able to customize other prefixes/sufixes and this extension does just that.So I wanted an extension that not only allowed me to use whatever set of emojis I wanted, I also wanted to be able to customize other prefixes/sufixes and this extension does just that.&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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Fcommitmsgtemplates.png%26w%3D3840%26q%3D75" 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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Fcommitmsgtemplates.png%26w%3D3840%26q%3D75" alt="My set of pre-defined commit messages" width="589" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So after I stage the files, I have a keyboard shortcut that triggers a commit and the commit message prompts. The first one is the image above and I use the following configuration for the following prompts&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Empty"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"⚒️ part of"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A commit that progresses a task or an epic"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"✅ closes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A commit that marks the finish of a task or an epic"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"💀 deprecates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A commit that deprecates something"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"scope"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🕹️game"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Game related changes"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"📦assets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Adds/Moves assets in the codebase"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"🛠️tools"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Tooling related changes"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"📐ui"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Changes related to the UI"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vscodeGitCommit.template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"{prefix}: {message}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"  {scope}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"    {action} {taskCode}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"    by David Morais &amp;lt;david@davidmorais.com&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The task code is a custom field and the third/fourth prompt (depending on whether I comment the scope out on a specific project that does not call for it) and I end up with a commit message like the one described in the &lt;code&gt;vscodeGitCommit.template&lt;/code&gt; config field.&lt;/p&gt;

&lt;h2&gt;
  
  
  Theming 🎨
&lt;/h2&gt;

&lt;p&gt;Unless I'm working under heavy sunlight and I need to switch to a light theme, this is how usually VS Code looks like to me.&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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Fvscodewiththemes.png%26w%3D3840%26q%3D75" 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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Fvscodewiththemes.png%26w%3D3840%26q%3D75" alt="My current VS Code setup with themes" width="1917" height="1034"&gt;&lt;/a&gt;&lt;br&gt;
Coincidentally, all of these theming extensions are at the bottom of my extensions list, when sorted by installs 🎯 So let me tell you a bit about them.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=antfu.icons-carbon" rel="noopener noreferrer"&gt;Carbon Product Icons&lt;/a&gt; 🔗
&lt;/h3&gt;

&lt;p&gt;It took me some time to realize that the icons for "Explorer," "Source Control," "Debug," and similar features can be customized. So I searched for "Product Icons" extensions and this is the set of icons I liked the most.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=vira.vsc-vira-theme" rel="noopener noreferrer"&gt;Vira Theme&lt;/a&gt;🔗
&lt;/h3&gt;

&lt;p&gt;A fairly recent change I made in my icons was to use the nice icons provided by this theme package, which also adds a few more themes to choose from. I liked the fact they had customized icons for alot of libraries. I'm using the Vira Teal Icons variant.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=DavidMorais.dark-magic-themes" rel="noopener noreferrer"&gt;Dark Magic Themes&lt;/a&gt; 🔗
&lt;/h3&gt;

&lt;p&gt;Time for a shameless self plug here. I created these a few years ago because I wanted something along the lines of Tokyo Night or Dracula but with a teal accent. This package includes 2 personal themes (Dark Magic + Night Variant) with my personal synthax color scheme.  Then I also added a Nord, Dracula, Tokyo and a Frankenstein (green-ish) variants with custom syntax colors. A light variant is in the works and will be out soon ™️.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=million.million-lint" rel="noopener noreferrer"&gt;Million Lint&lt;/a&gt;🔗
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;a linter for your React components' performance.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is an extension I've used in some personal projects to a moderate extent. This extension essentially plugs and listens to your development server and not only points out performance issues, but can also suggest some fixes or improvements. It can be useful to have it on as it does not take much resources to run in dev mode, just install the &lt;a href="https://app.capacities.io/c85d4dad-a1b8-40a8-9519-1c5e0ca71a2e/ff7b5f0d-dc89-4592-ad5a-d1bdbb20ed45" rel="noopener noreferrer"&gt;npm package&lt;/a&gt; and initialize it and Million Lint will start to point out all those unecessary re-renders in your apps' components.&lt;/p&gt;

&lt;p&gt;⚠️ I think this one is about to be sunset due to the removal of their extension README and their &lt;a href="//million.dev"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=kingwl.vscode-vitest-runner" rel="noopener noreferrer"&gt;Vitest Runner&lt;/a&gt;🔗
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;fastest way to run specs directly in VS Code&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This extension does exactly what its name suggests: it adds "Run" and "Debug" buttons to all my spec files. This saves me from having to manually copy the file path and type the corresponding &lt;code&gt;vitest&lt;/code&gt; command each time. That’s pretty much it. While there are similar extensions for Jest, as well as many that add a test explorer UI to the sidebar, I prefer the simplicity of having just a few extra buttons in my code over additional UI elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=mattpocock.ts-error-translator" rel="noopener noreferrer"&gt;Total Typescript&lt;/a&gt;🔗
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;translates Typescript errors to human language&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I've had this extension installed for a while, pretty much since I added Typescript to my stack some 3 or 4 years ago. Total Typescript helps with those sometimes cryptical Typescript error messages and translates them into something a &lt;del&gt;sort of&lt;/del&gt; human, like a developer, can read. It's an extension I totally recomend if you're getting into TS as it can make the learning curve a bit less steep and save you some hours in debugging those initial TS errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=eliostruyf.vscode-front-matter" rel="noopener noreferrer"&gt;FrontMatter CMS&lt;/a&gt; 🔗
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;turns VS Code into a CMS&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This is a true gem 💎 and what I use to manage the posts on my &lt;a href="//www.davidmorais.com"&gt;website&lt;/a&gt;. It gives VS Code magic CMS powers, giving it a nice UI where you can manage your content. You can have custom fields, you can have it read directly from your markdown files' YAML properties and it even gives you SEO improvement suggestions for your content.&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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Ffrontmattercms.png%26w%3D3840%26q%3D75" 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%2Fdavidmorais.com%2F_next%2Fimage%3Furl%3D%252Fimg%252Fcontent%252Ffrontmattercms.png%26w%3D3840%26q%3D75" alt="Frontmatter CMS in action" width="704" height="1744"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I highly recommend this extension; however, I have to admit that I usually disable it when working on projects that don't involve a CMS or markdown files.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://marketplace.visualstudio.com/items?itemName=mattpocock.ts-error-translator" rel="noopener noreferrer"&gt;Gistpad&lt;/a&gt; 🔗
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;helps you access your gists from VS Code&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;GistPad is my favorite way to work with GitHub gists. It adds a product icon to your sidebar (personally, I keep all non-local tools like Copilot and GistPad on the secondary sidebar), letting you access both your public and private gists right from within VS Code. My main use for this is storing configuration files—like zsh, PowerShell scripts, custom themes, and more. Whenever I encounter software that uses plain text for its configuration (which is common with many developer tools), I save the config file as a private gist. Then, whenever I need to set up that software on a new machine, I simply open the config file in VS Code, click the GistPad icon, find the relevant gist, and copy-paste the content where I need it.&lt;/p&gt;

&lt;p&gt;If you don’t use gists or already have a preferred method for managing configuration files, GistPad might not offer much value for you.&lt;/p&gt;

&lt;p&gt;However, if you ever find yourself juggling different configs across machines, or just want a lightweight way to sync snippets and templates, GistPad is worth a look. The extension also supports more than just text files—you can organize markdown notes, code samples, and even small datasets, all conveniently searchable within the same interface. Plus, thanks to its seamless GitHub integration, any edits you make in VS Code are instantly saved back to your gists, ensuring you never lose track of important tweaks or updates. Overall, it’s a simple but surprisingly powerful addition to any developer’s toolkit.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;That wraps up my tour of lesser-known—but genuinely useful—VS Code extensions that make my coding life a little better every day. While it’s easy to stick with the tried-and-true plugins everyone talks about, sometimes it’s these hidden gems that solve problems you didn’t even realize you had. I hope you found at least one new extension to try, or a tweak that gives your setup a fresh spark of inspiration. If you’ve got any underrated favorites of your own, let me know—I’m always looking to test out new tools. Happy coding!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>vscode</category>
      <category>extensions</category>
      <category>programming</category>
    </item>
    <item>
      <title>Updating my website's tech stack in 2025</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Thu, 27 Feb 2025 19:55:50 +0000</pubDate>
      <link>https://dev.to/davidmorais/updating-my-websites-tech-stack-in-2025-35n3</link>
      <guid>https://dev.to/davidmorais/updating-my-websites-tech-stack-in-2025-35n3</guid>
      <description>&lt;h2&gt;
  
  
  Intro 🤓
&lt;/h2&gt;

&lt;p&gt;The world of front end development is always changing and in order to keep up-to-date with the latest technologies I like to take on personal projects on my free time. My website/blog is one of these projects that I have built, and despite the semi-neglect it has seen over the past few years, I have always been proud of having it and showcasing it to people.&lt;/p&gt;

&lt;p&gt;As one can infer from my post history, stuff happened in my personal life that has left me with little to no time for writing tech articles or even work on my side projects. I gave up on finishing &lt;a href="https://epochrift.netlify.app" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt; about 1 year and a half ago after realizing I was way too optimistic with the initial scope of the project and my pixel art making skills, and since then I have only started local repositories that eventually were lost when my old computer was bricked. &lt;/p&gt;

&lt;p&gt;So in an effort to keep up to date and also to give my website a much needed tech update, along with a visual overhaul. In this post, I will detail as much as possible what hardships I faced, decisions I took and the &lt;a href="https://davidmorais.com" rel="noopener noreferrer"&gt;final result&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  How it was 🦕
&lt;/h3&gt;

&lt;p&gt;My website's previous iteration was built in 2021. It was bootstrapped using (the now deprecated) &lt;a href="https://github.com/facebook/create-react-app" rel="noopener noreferrer"&gt;Create React App&lt;/a&gt; and it took approximately 2 months to build. The home page included a bunch of photos that I had taken myself of my desk and keyboard as background for several sections and it included most of the information on the website. In the middle of the page I put the &lt;code&gt;SkillsTerminal&lt;/code&gt; (which also features in the current version), which provided visitors with a familiar and mobile-ready UI which included my tech skills, aswell as a bit of information on my work and project history.&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%2Fe6yr6rlt9nd8v4s8bu1q.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%2Fe6yr6rlt9nd8v4s8bu1q.png" alt="A comparison betwen package.json" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The other page was a list of my blog posts that were posted in &lt;a href="https://hashnode.com/" rel="noopener noreferrer"&gt;Hashnode&lt;/a&gt;, fetched using Graph QL using Hashnode's API. The posts would then be shown when the user navigated to &lt;code&gt;/post/&amp;lt;slug&amp;gt;&lt;/code&gt; , after triggering another request to Hashnode's API. I also built my own solution for i18n and theming and relied on &lt;code&gt;styled-components&lt;/code&gt; to do most of the CSS heavy lifting and customization.&lt;/p&gt;

&lt;h3&gt;
  
  
  Javascript to Typescript ✨️
&lt;/h3&gt;

&lt;p&gt;Very early on when planning this update I was decided on making the whole thing in Typescript as the previous itteration of the website was one of the last projects I built entirely using only Javascript.&lt;/p&gt;

&lt;p&gt;I could have spent some weeks migrating some components I previously used, but along with the decision to migrate to Typescript, I also came to the conclusion that I was better off building the UI from scratch. &lt;/p&gt;

&lt;p&gt;The only code I felt I could migrate to Typescript was my data/configuration files. For example, for my resume, I wrote a &lt;code&gt;.ts&lt;/code&gt; file with a single exported variable that contained my work history information. My resume, contacts and the data in the &lt;code&gt;SkillsTerminal&lt;/code&gt; (I will get to it 🙏) was all that I felt I had to actually write some interfaces for.&lt;/p&gt;

&lt;h3&gt;
  
  
  Styled Components to TailwindCSS 🌬️
&lt;/h3&gt;

&lt;p&gt;So, along with the decision to use Typescript and start a codebase from scratch, I also decided that for once I was going to save some time and use one of those shiny new primitive libraries. I did not really want a complete set of UI components, but rather some primitives that worked out of the box but were also easily customizeable. In my last React Native project, I had worked with &lt;a href="https://tamagui.dev/" rel="noopener noreferrer"&gt;Tamagui&lt;/a&gt; and I loved it, I also really like the components provided in &lt;a href="https://www.heroui.com/" rel="noopener noreferrer"&gt;Hero UI&lt;/a&gt; (formerly known as Next UI), so I was going for something with the same visual style, but lighter.&lt;/p&gt;

&lt;h4&gt;
  
  
  shadcn/ui 🧩
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; is definetly the new cool kid on the block as far as UI libraries are concerned, despite the fact that they clearly state in their documentation &lt;em&gt;"This is not a component library. It is how you build your component library".&lt;/em&gt; This was exactly what I needed.&lt;/p&gt;

&lt;p&gt;This library does not provide developers with the usual &lt;code&gt;npm i shadcn/ui&lt;/code&gt; + &lt;code&gt;import Component from "shadcn/ui"&lt;/code&gt; workflow. Instead, they give the code to the actual component so that you use and import it in your codebase. Whereas the usual paradigm means importing a file from deep inside &lt;code&gt;node_modules&lt;/code&gt; and having a wrapper around it, be it for style or functional purposes, I am now finding myself not only using only what I need from a component library, but also being able to customize my own components much easier as I am either dealing with plain HTML elements or &lt;a href="https://www.radix-ui.com/" rel="noopener noreferrer"&gt;Radix&lt;/a&gt; primitives.&lt;/p&gt;

&lt;p&gt;Setting it up was not hard at all, I just followed the &lt;a href="https://ui.shadcn.com/docs/installation/manual" rel="noopener noreferrer"&gt;documentation for a manual installation&lt;/a&gt; and went with it. The only minor caveat I did find is that I had to add &lt;code&gt;@plugin "tailwindcss-animate";&lt;/code&gt; to the top of my &lt;code&gt;global.css&lt;/code&gt; file since &lt;code&gt;tailwindcss-animate&lt;/code&gt; doesn't support the latest version of Tailwind.&lt;/p&gt;

&lt;h4&gt;
  
  
  Goodbye &lt;code&gt;styled-components&lt;/code&gt; 👋
&lt;/h4&gt;

&lt;p&gt;One of the fads that I have never indulged in for the past few years was the &lt;a href="https://tailwindcss.com/" rel="noopener noreferrer"&gt;Tailwind CSS&lt;/a&gt; fever. To me, this was just another Bootstrap waiting to be replaced by a more modern, more complete and lighter version of itself. And over the years, I've noticed that some component libraries would often indicate in their documentation that they recently migrated away from &lt;code&gt;styled-components&lt;/code&gt;. Looking into it, I soon discovered that I would be facing the same decision.&lt;/p&gt;

&lt;p&gt;You see, &lt;code&gt;styled-components&lt;/code&gt; and other CSS-in-JS has a huge impact on performance and size for the website as it includes more styles that it needs to (and this can be optimized to a point, but I wanted a simpler approach) and the styles are applied at runtime, adding overhead for the browser to parse and apply the styles.&lt;/p&gt;

&lt;p&gt;I did try to make it work with Tailwind and styled-components, but ultimately I was seeing some performance issues and weird flickers that were clearly coming from these two conflicting engines and I decided to ditch styled-components all together, after some 5 years of being my go-to CSS solution, it's time to say goodbye and welcome more modern, complete and lighter version of it 🤡&lt;/p&gt;

&lt;h3&gt;
  
  
  Ditching API based articles 🖊️
&lt;/h3&gt;

&lt;p&gt;Another decision I had taken previously to starting working on this, was to stop fetching the posts on the client side. This is terrible for SEO and makes me have to rely on a thirdparty API to keep the posts available. And in my experience, there were breaking changes to the Hashnode API a few years ago that made it so that my posts were unreachable for quite some time until I finally noticed they were not working when I happened to visit the website. &lt;/p&gt;

&lt;p&gt;Besides adding error monitoring to the website, I also decided that the way to go was to render the markdown articles on the server. In doing so I gave Astro a spin, but after being unable to get the Tailwind CSS working, I decided to settle on Next.JS. It is a familiar framework, but I still had to learn how to use the relatively new App Router, which was new to me.&lt;/p&gt;

&lt;h3&gt;
  
  
  Next.js' App Router 🛣️
&lt;/h3&gt;

&lt;p&gt;Next is shifting towards what they're calling App Router. The previous itteration, known as Pages Router is not compatible with those shiny new React Server Components I mentioned earlier. The main difference to me was using simple &lt;code&gt;fetch&lt;/code&gt; and &lt;code&gt;async/await&lt;/code&gt; syntax to fetch the server side props. In this case, I had a node script that relied on &lt;code&gt;fs&lt;/code&gt; to retrieve the markdown files and a library called &lt;a href="https://www.npmjs.com/package/gray-matter" rel="noopener noreferrer"&gt;gray-matter&lt;/a&gt; to retrieve their YAML metadata properties. Then all I had to do was transform my &lt;code&gt;[slug]&lt;/code&gt; page into an &lt;code&gt;async&lt;/code&gt; function and call the function that fetched the posts from the filesystem.&lt;/p&gt;

&lt;p&gt;The other main difference is that instead of relying on a file per page, this allows me to make hierarchical routes, creating nested layouts of components that may need to wait for server data much easier. &lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion 🚀
&lt;/h4&gt;

&lt;p&gt;Revamping my website proved to be a rewarding journey filled with both challenges and learning experiences. Transitioning from JavaScript to TypeScript not only improved the robustness of my code but also enhanced the maintainability and scalability of the project. The move from styled-components to Tailwind CSS streamlined the styling process, resulting in cleaner code and better performance, while embracing the component-first approach with &lt;a href="https://ui.shadcn.com/" rel="noopener noreferrer"&gt;shadcn/ui&lt;/a&gt; brought more customization flexibility.&lt;/p&gt;

&lt;p&gt;Furthermore, switching from an API-based article system to server-rendered markdown posts was a win for SEO and reliability, allowing me to present my content more efficiently and without external dependencies. With Next.js' App Router and the integration of React server components, I was able to reinforce structure and enhance performance, thoroughly modernizing the tech stack of my site.&lt;/p&gt;

&lt;p&gt;Reflecting on these updates, it's clear that the landscape of web development is ever-evolving, pushing us to adapt and iterate continuously. This project not only rejuvenated my personal website but also rekindled my passion for front-end development, reminding me that keeping pace with new technologies is both imperative and invigorating. As I move forward, I am excited to explore more innovative tools and techniques that can further elevate my web development projects.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>nextjs</category>
      <category>typescript</category>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Testing Phaser Games with Vitest</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Sat, 10 Sep 2022 22:21:21 +0000</pubDate>
      <link>https://dev.to/davidmorais/testing-phaser-games-with-vitest-3kon</link>
      <guid>https://dev.to/davidmorais/testing-phaser-games-with-vitest-3kon</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As you may know, I am currently developing my own solo indie game, it's called &lt;a href="https://epochrift.com/" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt; and it consists on a 2D roguelike platformer in which you can only play as spell casters. It is being built using one of the most well-known JavaScript gaming frameworks: &lt;a href="https://phaser.io/" rel="noopener noreferrer"&gt;PhaserJS&lt;/a&gt;. I picked this framework because while I am familiar with JavaScript, I am not too familiar with the game development scene and how everything works.&lt;/p&gt;

&lt;p&gt;In this article, I will let you know how I have built a test driven environment for some of my game logic.&lt;br&gt;
So far, I have been focusing on building the essential systems and logic, basically laying down the groundwork so that  in the near future  I can start incrementing on it with new game content. As I have learned recently, this is commonly referred to as 'pre-production' stage whereas  the next stage consists on re-using the basis I've built so far to add new stuff, like monsters, spells and levels.&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%2Fff6zwh55266z00scsswk.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%2Fff6zwh55266z00scsswk.png" alt="Epoch Rift, Visceral Hills" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Game Logic
&lt;/h2&gt;

&lt;p&gt;One of these systems which was particularly hard to implement is what I call the &lt;strong&gt;Portal System&lt;/strong&gt;. This system was responsible for picking up the Portal tiles from Tiled and randomly assign destinations based on a set of conditions.&lt;br&gt;
Now is the time when I must come clean and let everyone know I am not the best at cracking algorithms. So, the initial implementation of this system was a mess, the resulting code was big, filled with comments to remind myself what was happening.&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%2Fxs1ssrqww0yxmjlm2c1u.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%2Fxs1ssrqww0yxmjlm2c1u.png" alt="Portal system diagram" width="558" height="321"&gt;&lt;/a&gt;&lt;br&gt;
After a few tweaks and itterations on this, I was ready to refactor the whole thing from scratch, however, this time I was ready to give Test Driven Development a chance.&lt;/p&gt;
&lt;h3&gt;
  
  
  Test Driven Development (TDD)
&lt;/h3&gt;

&lt;p&gt;If a recruiter reaches out to me with a Job Description that points towards heavy usage of TDD, I tend to discard it.&lt;br&gt;
This is probably because, as a front end developer, I have been traumatized by TDD zealots in the past who insisted that absolutely everything must be tested, going to the extreme of testing the browser's API to check if a button's &lt;code&gt;onClick&lt;/code&gt; is fired, for example.&lt;/p&gt;

&lt;p&gt;In my opinion, if you take TDD too seriously, it becomes counter-productive and a waste of everyone's time. I should not need to test if regular HTML &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; is working properly, for two reasons: 1- The HTML spec is not going to deprecate such a central element anytime soon and 2- In the highly unlikely chance that regular HTML &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; elements stop working on a certain browser overnight, I am powerless to fix them. The same can be said for game development, &lt;strong&gt;I do not want to test something that is already provided by the framework/tool I rely on&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;You can probably tell by now I am quite negligent when it comes to writing tests. I prefer to start writing code and test later, and then I test manually, like a tech-illiterate monkey 🐒 However,  for these complex systems, I figured I could reverse my usual process and &lt;strong&gt;start by writing a few conditions&lt;/strong&gt; that my system should satisfy, create a simple &lt;strong&gt;function that receives parameters and returns an output&lt;/strong&gt; and then &lt;strong&gt;adjust the code&lt;/strong&gt; of that function until my assertions would pass.&lt;/p&gt;

&lt;p&gt;My first proof-of-concept was a CodeSandbox in which I copy-pasted a tiled map &lt;code&gt;json&lt;/code&gt; and tested a few conditions with the obscure &lt;code&gt;console.assert&lt;/code&gt; method. It was fine for a PoC, but for the game itself I needed something more robust. For example, my humble PoC was only testing one static tiled map, I needed to test all maps and I needed to ensure my function would still work if the maps were updated. It was settled, I had to bring testing to my Phaser game.&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%2Fam9c6rtxhkh7xhvropa7.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%2Fam9c6rtxhkh7xhvropa7.png" alt="Epoch Rift portal" width="800" height="336"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Vitest
&lt;/h2&gt;

&lt;p&gt;In the past I have worked with several JavaScript testing frameworks, mainly Jest, Enzyme and react-testing-library, however I only used these when testing React applications and the biggest hardship I always faced was setting up the tests. Usually we need to mock most of the stuff required by our React application, be it request calls, functions, or request responses. I did not want to do that. By this time, I was fairly inclined to go with Jest due to my familiarity with it, however...&lt;/p&gt;

&lt;p&gt;When I was doing this, I had recently moved the codebase from Webpack to Vite, with amazing results in terms of speed. I totally intend to write an article on this migration, but the point is that the same guys behind Vite had also this testing framework, called &lt;a href="https://vitest.dev/" rel="noopener noreferrer"&gt;Vitest&lt;/a&gt;, which was supposed to be better than all frameworks out there while &lt;strong&gt;offering compatibility with the Jest API and plugins&lt;/strong&gt;. What sold it to me was the fact that since &lt;strong&gt;I already have Vite configured,  Vitest was fairly easy to set-up&lt;/strong&gt;, whereas had I gone with Jest, I would have spent hours configurating the thing and added a ton of dev-dependencies to transpile my Typescript code.&lt;/p&gt;
&lt;h3&gt;
  
  
  Vitest vs Jest
&lt;/h3&gt;

&lt;p&gt;If I were to summarize it, I would say that &lt;strong&gt;Vitest is Jest built on top of Vite&lt;/strong&gt;. It is compatible with the widely used Jest API and offers support for Jest Plugins. The difference is that I don't need to add another configuration file for my alias and I don't need to transpile my TypeScript code, because Vitest relies on the same configuration as Vite. Actually setting it up is quite simple. &lt;br&gt;
If you want to compare Vitest with other popular testing frameworks, you can read more on that on their &lt;a href="https://vitest.dev/guide/comparisons.html" rel="noopener noreferrer"&gt;Comparisons&lt;/a&gt; page.&lt;/p&gt;
&lt;h3&gt;
  
  
  Setting Up
&lt;/h3&gt;

&lt;p&gt;First we need to add the package to our dev dependencies.  Run any of the following commands, depending on your package manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;vitest &lt;span class="nt"&gt;--save-dev&lt;/span&gt;
yarn add vitest &lt;span class="nt"&gt;-D&lt;/span&gt;
pnpm add vitest &lt;span class="nt"&gt;-D&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After we have the package,  we need to do is install the package and add are a few lines in my &lt;code&gt;vite.config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;/// &amp;lt;reference types="vitest" /&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;// ➕ We need to add this for TypeScript support. If you aren't, you don't need this&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// ... rest of my config&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;globals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// ➕ I'm using globals.If you aren't, you don't need this.&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;You may also want to add a script to our &lt;code&gt;package.json&lt;/code&gt; to run &lt;code&gt;vitest&lt;/code&gt; .&lt;br&gt;
Now we are ready to create our first spec file.  Which can be placed anywhere and simply needs to follow the &lt;code&gt;&amp;lt;file&amp;gt;.spec.ts&lt;/code&gt; naming convention.&lt;/p&gt;
&lt;h4&gt;
  
  
  Testing Game Logic
&lt;/h4&gt;

&lt;p&gt;So let's go back to my game's Portal system. As you've guessed, it eventually evolved from CodeSandbox and was introduced to my &lt;code&gt;Portals&lt;/code&gt; class. For context, this class is responsible for picking up a Tiled layer, rendering the portal sprites on their correct location and what happens when a player interacts with it. As you have probably guessed by now, my big ugly algorithm was part of the player interaction.&lt;/p&gt;

&lt;p&gt;Testing this whole thing would require me to create a whole environment for importing Phaser and actually rendering the sprite on &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt;. This was not good. Not that it should be hard to setup Phaser in an headless environment, but what good would it do knowing that my sprite was rendering properly, if all I wanted to test was the result of a function that runs inside the sprite class.&lt;/p&gt;

&lt;p&gt;Back when I was looking for the best tool to tackle this problem, I stumbeld upon &lt;a href="https://phaser.discourse.group/t/unit-testing/2922/2" rel="noopener noreferrer"&gt;this comment&lt;/a&gt; on a Phaser forum. It suggests running the game logic in a &lt;code&gt;webWorker&lt;/code&gt; and only using Phaser to display stuff. I already have some of the game logic isolated in a &lt;code&gt;procs&lt;/code&gt; folder, so I moved the logic into a single function which would split the entire portal destination assignment logic to a single function which would output an object and the sprite would then use that object to deal with the interaction of actually teleporting the player to its destination. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;To avoid having to mock anything, I replaced my &lt;code&gt;Phaser.Math&lt;/code&gt; usage with hand made functions&lt;/strong&gt; since I would have to write them anyway to mock them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;I've been writing about this Portal system for a while now and I still haven't explained what exactly it does.&lt;br&gt;&lt;br&gt;
In Epoch Rift, each level has a certain number of rooms. The player spawns in the same room. Each room has two portals, one origin portal from which the player arrives and a departure portal, which teleports the player to a random origin portal.&lt;/p&gt;

&lt;p&gt;My function basically takes all the portals and creates a &lt;code&gt;rooms&lt;/code&gt; object that is later used for reference whenever a player comes interacts with the portal.&lt;/p&gt;

&lt;p&gt;Simple, right ? &lt;/p&gt;

&lt;p&gt;Let's start by writing fairly simple test cases. Let's assume that &lt;code&gt;assignRoomsLogic&lt;/code&gt; is my function:&lt;br&gt;
⚠ This code is only an example improved for readability. I did not use the real Tiled layers nor the real function result to save space and to make it easier to understand what's going on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vitest&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./map.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;assignRoomsLogic&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Portals&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Logic for assigning rooms to portals&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;portals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPortalsFromMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;assignRoomsLogic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;portals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;it&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="nb"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 📌 has one spawn point`&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;portals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;portal&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;portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSpawn&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
      &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;it&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="nx"&gt;mapName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 📌 has the same amount of origin &amp;amp; departure portals`&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originPortals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;portals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;portal&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;portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;departurePortals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;portals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;portal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;origin&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originPortals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;departurePortals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="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;If you have worked with Jest, this should look familiar. Inside a &lt;code&gt;describe&lt;/code&gt; I am basically creating my test case, I get all the &lt;code&gt;portals&lt;/code&gt; from the map and create the resulting &lt;code&gt;rooms&lt;/code&gt;.  At this point, I am just ensuring that I did not fuck up when creating the Tiled map.&lt;/p&gt;

&lt;p&gt;In the first case I check that only one of the layer tiles has the &lt;code&gt;isSpawn&lt;/code&gt; property. On the second case checking we have the same number of origin and non-origin (departure) portals.  Let's proceed with testing the &lt;code&gt;assignRoomsLogic&lt;/code&gt; functionality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;    &lt;span class="nf"&gt;it&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="nx"&gt;mapName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 📌 has the same amount of rooms as the portals`&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;uniqRoomsInPortals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nx"&gt;portals&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;room&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="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uniqRoomsInPortals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;it&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="nx"&gt;mapName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 📌 has all portals leading to a room`&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;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PortalRoom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;departure&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
          &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
      &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;it&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="nx"&gt;mapName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 📌 has the spawn portal as the last destination`&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gotoPortalIfNotSpawn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;room&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PortalRoom&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="p"&gt;...&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;room&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="p"&gt;};&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;spawnRoom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PortalRoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rooms&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="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PortalRoom&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;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSpawn&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spawnRoom&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastRoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;gotoPortalIfNotSpawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spawnRoom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastRoom&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;spawnRoom&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="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nf"&gt;it&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="nx"&gt;mapName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; 📌 does not have any repeated destination`&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destinations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;r&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PortalRoom&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;r&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;destinations&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;size&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rooms&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this snippet, the first test case creates a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set" rel="noopener noreferrer"&gt;Set&lt;/a&gt; with unique room ids and ensures that there are not any un-reachable rooms. On the second, I confirm that the number of departure portals is the same number of rooms and therefore all of them lead to a room. The third thest case runs a recursive &lt;code&gt;gotoPortalIfNotSpawn&lt;/code&gt; function to go to the next room and stop at the spawn (ie, back at the beginning). And last I assert  again with &lt;code&gt;Set&lt;/code&gt; that the unique number of destinations is the same as the rooms.&lt;/p&gt;

&lt;p&gt;After this, the I wrapped this in a &lt;a href="https://vitest.dev/api/#describe-each" rel="noopener noreferrer"&gt;describe.each&lt;/a&gt;, loaded multiple maps and ran the same test cases for all maps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LEVEL_MAPS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...];&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Logic for assigning rooms to portals&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;LEVEL_MAPS&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;for each map&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&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="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;mapName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;editorsettings&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="k"&gt;export&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;portals&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPortalsFromMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rooms&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;assignRoomsLogic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;portals&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="c1"&gt;// Code from above, using mapName for the output&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;
  
  
  Vitest UI
&lt;/h3&gt;

&lt;p&gt;There are a &lt;a href="https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer" rel="noopener noreferrer"&gt;couple&lt;/a&gt; of VS Code Extensions &lt;a href="https://marketplace.visualstudio.com/items?itemName=kingwl.vscode-vitest-runner" rel="noopener noreferrer"&gt;for Vitest&lt;/a&gt;, which has helped during this process. But I'd say the main feature that made me enjoy working with TDD was &lt;strong&gt;&lt;a href="https://vitest.dev/guide/ui.html" rel="noopener noreferrer"&gt;Vitest UI&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Like Vitest, this is also a breeze to set up. Simply run &lt;code&gt;yarn add -D @vitest/ui&lt;/code&gt; and add a script to your &lt;code&gt;package.json&lt;/code&gt; to run &lt;code&gt;vitest --ui&lt;/code&gt; (or just do it from the CLI, I am not your father).&lt;/p&gt;

&lt;p&gt;The result is a dev server with hot reload that watches over your &lt;code&gt;.spec&lt;/code&gt; files and runs the suites whenever you update something.&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%2Fkt9ry8t3eoc5j8kxx5wg.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%2Fkt9ry8t3eoc5j8kxx5wg.png" alt="Vitest UI" width="800" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To the TDD zealots, I have to concede, when all of those go green for the first time, it is quite the pleasing sensation ✨&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Use Cases
&lt;/h3&gt;

&lt;p&gt;After I was done with this I have already used this approach to refactor some code and will also apply it in some upcoming features for the game. &lt;/p&gt;

&lt;p&gt;Another good example, albeit a bit more straightforward to test is the player state logic. I wrote some basic conditions that test, for example, that a player can't shoot a spell if he's jumping.&lt;/p&gt;

&lt;p&gt;I also intend to test the monster's aggro logic to ensure they're properly interacting when in proximity with the player, and ensure that  the loot table generation algorithm is giving out correct results.&lt;/p&gt;

&lt;p&gt;Every game is  a bit different and will have different systems and different game logic requirements. Board and puzzle games may want to ensure their initial board is correctly initialized and meets certain conditions, for example, or enemy and difficulty algorithms for a different number of game genres.&lt;/p&gt;

&lt;p&gt;The important thing is that we isolate what we want to test and what should be handled by the framework/engine.&lt;/p&gt;

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

&lt;p&gt;This is a no-bullshit, no-time-wasting approach to testing logic and systems in your JavaScript games. The experience so far has been really smooth and intuitive. Vitest is a great tool, fast and compatibility with the widely known Jest framework, and if you are already using Vite to build your project, I cannot really recomend anything else. If you are like me and like to develop with HMR, Vitest UI is also a great tool to work with tests.&lt;/p&gt;

&lt;p&gt;I hope this was a fun and informative read. If you want to read and learn more about me, be sure to check out my &lt;a href="https://davidmorais.com/" rel="noopener noreferrer"&gt;website&lt;/a&gt; with my past posts. If you want to learn more about &lt;a href="https://epochrift.com/" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt;, checkout its official website and follow us on &lt;a href="https://witter.com/epoch_rift" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;br&gt;
Do you use a different approach to to game development testing ? Do you know any other alternatives to Vitest/Jest ? Feel free to share your thoughts in the comments 💪&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>testing</category>
      <category>gamedev</category>
      <category>vite</category>
    </item>
    <item>
      <title>My Path to Front End Development - 5 Years Later</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Thu, 21 Apr 2022 00:29:04 +0000</pubDate>
      <link>https://dev.to/davidmorais/how-i-became-a-software-developer-5-years-later-40pj</link>
      <guid>https://dev.to/davidmorais/how-i-became-a-software-developer-5-years-later-40pj</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Hello friends 👋&lt;/p&gt;

&lt;p&gt;I would like to share my path towards becoming a front end developer using free resources in hopes that this post clears any doubts some of you may have regarding switching careers. This is going to be a a long one, so you can skip to the bottom if you want to see a summarized timeline of my adventures in the IT world 🚀&lt;/p&gt;

&lt;p&gt;April 20th, 2017, was my first day at &lt;strong&gt;&lt;a href="https://frames.news/" rel="noopener noreferrer"&gt;Frames News&lt;/a&gt;&lt;/strong&gt;. It was my first professional experience as a software developer, age 24.&lt;br&gt;
Before that, I was working remotely as an Apple &lt;em&gt;advisor&lt;/em&gt; (ie: glorified call center operator), picking up calls for eight hours straight from people who had mostly had forgotten their Apple ID and couldn't be bothered to use Google to find that password recovery page. And before that, I went to college for four amazing years where I met really great people and got my languages degree.&lt;/p&gt;

&lt;p&gt;Alot of the developers I have met ever since I got into this area, also had changed careers in pursuit of a better life and other ambitions, so if you too are reading this and considering jumping to this area, I hope this post will help you in making this decision as I will share how I became the software developer I am today and some of the accomplishments I had ever since I decided that I wanted to turn my life around, all without spending any money.&lt;/p&gt;

&lt;h2&gt;
  
  
  In a past life
&lt;/h2&gt;

&lt;p&gt;I completed my languages degree in May 2016. By then I only had two previous work experiences:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I worked as a 'tour guide' for a short time in the summer of 2015. And by 'tour guide' I mean, my job was more delivering brochures in downtown Lisbon, in hopes that tourists would book day-long tours for the next day. The problem was that for the two weeks I worked there, we were a team of four guides that only had one tour group schedule a tour. I was lucky enough to volunteer to be the first 'tour guide' and actually did the tour while my colleagues were delivering brochures all day. They 'fired' half the team when they realized they were not making any money, luckily I was let go.&lt;/li&gt;
&lt;li&gt;In 2016 I worked at a support call center contractor with a marketing company as a client. As I'm a native english speaker, this was good pay, and having heard the stories my college colleagues would tell about working in a sales or outbound call center, I considered myself lucky I was only calling people to check why on earth they weren't scanning all their shopping with our &lt;em&gt;fancy&lt;/em&gt; bar code scanner. And by &lt;em&gt;fancy&lt;/em&gt;, I mean, outdated piece of crap that had alot of problems and was not to be replaced by the available new models unless the clients complained about some specific issues.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Needless to say, I did not enjoy any of these experiences. At the call center I met all sorts of people who gave me an insight of what the future held for me: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Guys who actually got into the client's bar code scanner thingy and actually PAID from their own pocket to buy the client's swag online, which they would proudly wear to work.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Old&lt;/em&gt; folks who clearly were there just to pass the time and make a little extra money.&lt;/li&gt;
&lt;li&gt;People who I can only politely describe as mentally challenged who managed to get fired for being inefficient at reading a script (I am not 100% sure to which degree on purpose).&lt;/li&gt;
&lt;li&gt;Some had been at the company with the same client, doing the same thing and reading the same scripts for a time that I do not consider to be within sanity, I'm talking 15 years of asking people why they were not scanning their shopping every god forsaken day.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the following year I was set to finish college and get a job as a languages teacher and while I did finish college with good grades and got my degree, I only managed to get an interview for a teaching job, a part time offer at a private school nearby that said they were only waiting to settle a few details and they would for sure call me back. They never did.&lt;/p&gt;

&lt;p&gt;I found a job as an Apple remote advisor which was like having a call center in my room. It paid better than average jobs and they did send me an iMac to allow me to work from home. I started in September 2016, was making about 900 euros per month, and during the first few months, since I had to connect the internet cable to the iMac, I was constantly playing Football Manager on my own computer while taking calls.&lt;/p&gt;

&lt;p&gt;The fact that I was working from home did very little to improve my opinion of this &lt;em&gt;kind of work&lt;/em&gt;. I will always remember kindly the phone call I received while in the middle of going number two because I was 1 minute late on my break, the endless wave of calls, 75% of which about the same issue, that went on for eight straight hours and the weekly rotating schedule which could have me working from 8h-17h on a week and 12h-21h the next one. After a few months I was basically looking for a way out.&lt;/p&gt;

&lt;p&gt;The realization that if I did nothing my life would go on like this and in twenty years I would be just like one of the fellows I met on my first experience, was my first step towards becoming a software developer. There, I realized two things:  &lt;strong&gt;I would have to dedicate alot of extra time&lt;/strong&gt; to it and &lt;strong&gt;the sooner I started, the sooner I would be able to leave my current job&lt;/strong&gt; . &lt;/p&gt;

&lt;h2&gt;
  
  
  Jack of All Trades, Master of None
&lt;/h2&gt;

&lt;p&gt;By the time I was looking into alternatives, I already had a few options, all of them based on my personal hobbies and all of them with alot of competetion. I remember thinking: "&lt;em&gt;I can play guitar and piano and I also love playing video games, so for sure, a YouTube career is within my grasp&lt;/em&gt;" and who knows...&lt;/p&gt;

&lt;p&gt;Around this time, a very close friend from high-school came to visit and we had one of the most important conversations in my life, first because it introduced me to &lt;a href="https://freecodecamp.com" rel="noopener noreferrer"&gt;FreeCodeCamp&lt;/a&gt;, second because that's when I learned the expression &lt;strong&gt;"Jack of All Trades, Master of None"&lt;/strong&gt;. Imagine a game of roulette, spending a few years pursing a gamer/musician career would be the equivalent of randomly betting on numbers 23 and 8. &lt;/p&gt;

&lt;p&gt;I had already 'coded' some games while in high-school with a very specific game engine (that &lt;a href="https://byond.com" rel="noopener noreferrer"&gt;still exists&lt;/a&gt;), but I knew nothing of any real programing language. My friend knew this, in fact, we actually worked on a senior year project which involved creating a website. In this conversation, my friend recomended I checked out FCC and painted me a picture of what he heard about the IT scene:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Crazy wages. Get a Tesla in 2 years.&lt;/li&gt;
&lt;li&gt;Anyone can become the next Steve Jobs.&lt;/li&gt;
&lt;li&gt;Party during work hours. Drink during work hours.&lt;/li&gt;
&lt;li&gt;No extra hours.  If extra hours were required, pizza for everyone.&lt;/li&gt;
&lt;li&gt;Huge demand for anything IT related. You can fix printers ? Here's 100k/yr !&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of this sounded great (and not even close to reality in most cases), there was just this small problem that I had no idea how to actually develop software and, like I said above, already knew how to play guitar and video games 🙃. I had previously tried to learn how to develop by watching some Udemy or Coursera videos, but that did not work so I checked out FreeCodeCamp and read some articles on people who managed to use this to turn their life around.&lt;/p&gt;

&lt;h2&gt;
  
  
  Becoming a Developer
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://freecodecamp.org" rel="noopener noreferrer"&gt;FreeCodeCamp&lt;/a&gt; is a website that offers you a complete course in Web Development, for free. At the time, the curriculum was slightly different and was split into Front-End, Backend and Full-stack certifications, now its less condensed, feel free to check it out on their &lt;a href="https://www.freecodecamp.org/" rel="noopener noreferrer"&gt;website&lt;/a&gt;. To me, I really liked how they introduced new lessons and asked you to do an exercise on it.&lt;/p&gt;

&lt;p&gt;In a few weeks I had stopped playing Football Manager on my computer and managed to do some lessons while taking calls. I felt the HTML and CSS modules were really easy.  My first project was a simple Bob Dylan tribute page using only HTML, &lt;a href="https://getbootstrap.com/" rel="noopener noreferrer"&gt;Bootstrap&lt;/a&gt; and CSS. I started to feel some difficulty when I started with the JavaScript lessons.&lt;/p&gt;

&lt;p&gt;However, the thought of waking up knowing I would have to face eight hours a day of talking to pissed off Apple customers who either forgot their password or bought a bricked phone, pushed me forward. During this time I was fully commited to getting my life on track and I had also decided that I would quit my job when I finished the Front End Development Program. &lt;/p&gt;

&lt;p&gt;I think what really worked for me is that FCC lessons did not provide the full picture and instead encouraged me to go and search out how to accomplish something and actually understanding what was happening.&lt;/p&gt;

&lt;p&gt;So I continued learning JavaScript and eventually &lt;a href="https://jquery.com/" rel="noopener noreferrer"&gt;jQuery&lt;/a&gt; , did a &lt;a href="https://codepen.io/OPaiTaCa/pen/NdGGOM" rel="noopener noreferrer"&gt;Pomodoro Timer&lt;/a&gt; and a &lt;a href="https://codepen.io/OPaiTaCa/pen/MJeQjM" rel="noopener noreferrer"&gt;Simon Game&lt;/a&gt; on &lt;a href="https://codepen.io/" rel="noopener noreferrer"&gt;CodePen&lt;/a&gt; and my final project was making my own &lt;a href="https://davidsmorais.github.io/#/" rel="noopener noreferrer"&gt;GitHub Profile&lt;/a&gt; page. I got my certificate for the Front End Development Program on January 16th, 2017 and I would quit my job soon after. With this certificate I started sending CV's and applying for every Junior Developer role.&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%2F1430z46ci17aezek7g3o.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%2F1430z46ci17aezek7g3o.png" alt="Certificate" width="621" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Job Hunting
&lt;/h2&gt;

&lt;p&gt;One of the first and most important things I did, advised by FCC, was to properly write my LinkedIn profile to something more resembling IT career. I was hesitant to do some steps, but I followed every single orientation and article and for the first time in my life, someone messaged me on LinkedIn 🤩 it was a recruiter who just wanted to become part of my 'network' and for some reason sent me a message 😢&lt;/p&gt;

&lt;p&gt;My friends, I am fully aware that this certificate and 'online course' is not the best answer when they ask you about your education on interviews. Still, I managed to go to get a few interviews on well known consultancy companies, suited up (yeah, &lt;strong&gt;I know 😅&lt;/strong&gt;) and most of the times I received the typical, "&lt;em&gt;We will reach out to you&lt;/em&gt;", some actually did, just to let me know that the final client was looking for a more experienced candidate.&lt;/p&gt;

&lt;p&gt;In one consultancy, I had a brief tech interview with the CTO and actually got an internship at that company, but the state denied to fund said internship and they were not willing to pay my full salary. The other occasion I came to getting my first job came in mid February 2017 when I got through a technical interview and was supposed to deliver my first take home assignment.&lt;/p&gt;

&lt;p&gt;They wanted me to build a table from the JSON, and I read somewhere in the documentation that they used some sort of HTML templating solution. The process of cloning a repo, the whole git workflow and the HTML template were entirely new to me. I remember I did most of it using Handlebars and CSS. Rejection came a few days later.&lt;/p&gt;

&lt;p&gt;During March I got some interviews in some consultancies and some start ups which kind of kept me going, however, nearly 2 months of rejection were starting to take a toll. I remember that I even sent my new 'developer' CV to some call centers and temp work agencies, but not even they were calling me back. I also started to get more selective in my job search and sent CVs only to start ups, who were more likely to look at my little portfolio and the effort I put into it.&lt;/p&gt;

&lt;p&gt;We were now in April and I was worried I made a terrible mistake by leaving my job. I had nothing to show for myself except for a few CodePen projects, an empty GitHub profile, and a PDF that says I did some coding exercises that you can just Google the answer for. And my parents were happy to point this out to me.&lt;/p&gt;

&lt;p&gt;And then, the guys from the code project I delivered in mid February called me and asked me if I was still available.&lt;/p&gt;

&lt;h2&gt;
  
  
  I AM DEVELOPER ! (sort of)
&lt;/h2&gt;

&lt;p&gt;The guy who was initially hired left for some reason, and I was more than happy to take the chance. I realize this bit of sheer luck has possibly enabled everything that came after, but I will be forever thankful to whoever poached that guy from the startup I was about to start working on.&lt;/p&gt;

&lt;p&gt;And so, April 20, 2017, was my first day at &lt;a href="https://frames.news/" rel="noopener noreferrer"&gt;Frames&lt;/a&gt;. My job would be to make charts using the &lt;a href="https://d3js.org/" rel="noopener noreferrer"&gt;D3&lt;/a&gt; library (which was also a part of the Free Code Camp curriculum) and vanilla Javascript. The company was quite small, there was a journalist who would use a backoffice to input data on chart templates, a senior developer to create and maintain the backoffice functionality and me, who would create and maintain the chart templates. Regarding the salary, I accepted the fact that I wouldn't be as productive as someone that graduated from a bootcamp or college so I was happy to take the 700 euro per month pay check.&lt;/p&gt;

&lt;p&gt;The first few weeks were tough, I rembember being asked to read the entire &lt;a href="https://github.com/getify/You-Dont-Know-JS" rel="noopener noreferrer"&gt;You Don't Know JS&lt;/a&gt; book with the implied threat of losing my job. FreeCodeCamp did give me the basic knowledge, but now I needed to learn about the foundations of the language I would be using for the next years. If you are a self taught developer, I totally recomend you read through this and other books that go in depth into the language features.&lt;/p&gt;

&lt;p&gt;While my senior developer made me feel dumb multiple times a day, I felt I was evolving as a developer and actually learning alot during my work day. The work environment was also something I had never witnessed before. I remember them laughing at me when I my &lt;em&gt;memory's muscle memory&lt;/em&gt; warned everyone I was going to take a leak. &lt;/p&gt;

&lt;p&gt;This experience was also invaluable because I had a walking fountain of knowledge sitting next to me and was really cool about answering my questions and pointing out all code style errors in countless PR reviews. I cannot count the amount of times he had to explain me the whole rebase workflow. What really helped me improve my Git knowledge was &lt;a href="https://gitkraken.com/" rel="noopener noreferrer"&gt;GitKraken&lt;/a&gt; and other similar tools.&lt;/p&gt;

&lt;p&gt;I eventually started to learn React and even started to lend a hand on the backoffice while my colleague would focus more on the backend and devops component, however, the company decided to stop development in the summer of 2018. The service and the templates I developed were active until the company stopped its service in March 2021.&lt;/p&gt;

&lt;h2&gt;
  
  
  Milestones &amp;amp; Timeline
&lt;/h2&gt;

&lt;p&gt;For someone who's considering changing careers into IT, I would like to point out that by the time my boss announced he was closing I was already receiving interview requests on a semi-daily basis and had little trouble in finding my next job, which doubled my salary and took me to travelling to the Netherlands for the first time in my life. I have never needed to apply for a job ever since.&lt;/p&gt;

&lt;p&gt;I am currently on my fourth job in IT, working for &lt;a href="https://www.pennylane.tech/" rel="noopener noreferrer"&gt;Pennylane&lt;/a&gt;, a french company with big plans for the Accounting area, in the meantime I have done a few open source projects and am currently developing a game with Typescript and React, &lt;a href="http://epochrift.com/" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt;, among other side projects.&lt;/p&gt;

&lt;p&gt;Here is a timeline of some of what I consider achievements ever since I started the FreeCodeCamp course&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2016

&lt;ul&gt;
&lt;li&gt;September

&lt;ul&gt;
&lt;li&gt;Started &lt;a href="https://freecodecamp.org" rel="noopener noreferrer"&gt;FreeCodeCamp&lt;/a&gt; Front End Development Program&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;2017

&lt;ul&gt;
&lt;li&gt;January

&lt;ul&gt;
&lt;li&gt;Completed the Front End Development Program&lt;/li&gt;
&lt;li&gt;Quit my job&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;April

&lt;ul&gt;
&lt;li&gt;Got my first job at &lt;a href="https://frames.news" rel="noopener noreferrer"&gt;Frames&lt;/a&gt; as a junior front end developer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;June

&lt;ul&gt;
&lt;li&gt;Completed &lt;a href="https://www.sololearn.com/home" rel="noopener noreferrer"&gt;SoloLearn&lt;/a&gt; JavaScript course&lt;/li&gt;
&lt;li&gt;Started learning &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;2018

&lt;ul&gt;
&lt;li&gt;March

&lt;ul&gt;
&lt;li&gt;Started developing &lt;a href="https://mdyna.netlify.app" rel="noopener noreferrer"&gt;MDyna&lt;/a&gt;, an open source markdown notes app with React and Electron&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;August

&lt;ul&gt;
&lt;li&gt;Completed &lt;a href="https://www.sololearn.com/home" rel="noopener noreferrer"&gt;SoloLearn&lt;/a&gt; Python course&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;September

&lt;ul&gt;
&lt;li&gt;Joined &lt;a href="https://www.intrasurance.com/" rel="noopener noreferrer"&gt;Intrasurance&lt;/a&gt; as a React Engineer via a consultancy&lt;/li&gt;
&lt;li&gt;Started learning &lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;Typescript&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt; 2019

&lt;ul&gt;
&lt;li&gt;November

&lt;ul&gt;
&lt;li&gt;Went to Websummit via &lt;a href="https://mdyna.netlify.app" rel="noopener noreferrer"&gt;MDyna&lt;/a&gt; and Github's Open Source Program&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;2020

&lt;ul&gt;
&lt;li&gt;January

&lt;ul&gt;
&lt;li&gt;Joined &lt;a href="https://gaspardbruno.com/" rel="noopener noreferrer"&gt;Gaspard+Bruno&lt;/a&gt; as a React &amp;amp; React Native developer&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;March

&lt;ul&gt;
&lt;li&gt;Completed &lt;a href="https://www.sololearn.com/home" rel="noopener noreferrer"&gt;SoloLearn&lt;/a&gt; Ruby course&lt;/li&gt;
&lt;li&gt;Stopped working on MDyna&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt; 2021

&lt;ul&gt;
&lt;li&gt;May

&lt;ul&gt;
&lt;li&gt;Started working on &lt;a href="https://epochrift.com" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt;, a roguelike game with Typescript and React&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;2022

&lt;ul&gt;
&lt;li&gt;December

&lt;ul&gt;
&lt;li&gt;Joined &lt;a href="https://www.pennylane.tech/" rel="noopener noreferrer"&gt;Pennylane&lt;/a&gt; as a Front End Developer&lt;/li&gt;
&lt;li&gt;Started working on &lt;a href="https://github.com/davidsmorais/kuro" rel="noopener noreferrer"&gt;Kuro&lt;/a&gt; a Microsoft To-Do desktop client for linux&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;April

&lt;ul&gt;
&lt;li&gt;Released &lt;a href="https://www.npmjs.com/package/dm-react-bifrost" rel="noopener noreferrer"&gt;React Bifrost&lt;/a&gt; my first NPM package&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;For any questions you can reach me on my &lt;a href="https://davidmorais.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; and follow me on &lt;a href="https://twitter.com/davidsmorais" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, you can also drop a coment below. I will do my best to try and clarify and doubts you may have regarding switching careers or what path to take.&lt;/p&gt;

</description>
      <category>career</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Git Worktrees - git's old hidden feature you never knew</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Wed, 13 Apr 2022 18:09:15 +0000</pubDate>
      <link>https://dev.to/davidmorais/git-worktrees-gits-old-hidden-feature-you-never-knew-43k1</link>
      <guid>https://dev.to/davidmorais/git-worktrees-gits-old-hidden-feature-you-never-knew-43k1</guid>
      <description>&lt;p&gt;Hello fellow developer 👋&lt;/p&gt;

&lt;p&gt;Have you ever been in one of the following situations ? &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You are developing a feature and your PM just had his dose of 'cool ideas' at lunch and really wants you to drop everything and work on his new brain child ?&lt;/li&gt;
&lt;li&gt;Your &lt;del&gt;young padawan&lt;/del&gt; junior  cannot, for his paycheck's sake, discover how to make something work and after a 2 hours call you just ask him to push his code so you can quickly take over ?&lt;/li&gt;
&lt;li&gt;You push to production, treat yourself a cake for a job well done, and start working on something else when the CI/CD starts screaming alerts on 3 different devices and you need to drop everything and put out a fire ?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For times like this, most people either do &lt;code&gt;git stash&lt;/code&gt; or a &lt;code&gt;git commit -m "wip"&lt;/code&gt; in order to save what they were doing so they can tend to other priorities.&lt;/p&gt;

&lt;p&gt;Quickly changing your` mindset to deal with another problem requires you to re-align your focus and get your bearings, now, depending on many factors changing branches may involve setting up a whole dev environment, like running migrations or installing dependencies, and that takes time.&lt;/p&gt;

&lt;p&gt;I was quite happy with following either one of the above mentioned methods during the entirity of my short career. That is, until I started working in a project with a huge number of developers, which translates in having to 'refresh' alot of stuff everytime you switch branches. Luckily, recently I came across a git feature that seems to have been introduced &lt;a href="https://github.blog/2015-07-29-git-2-5-including-multiple-worktrees-and-triangular-workflows/" rel="noopener noreferrer"&gt;way back in 2015, with the release of Git 2.5&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are Git Worktrees ?
&lt;/h2&gt;

&lt;p&gt;Git Worktrees allow you to quickly switch between branches without having to re-initialize your whole dev environment. When you do &lt;code&gt;git worktree add&lt;/code&gt; you create what is called a "linked worktree" . This is the worktree equivalent of doing a &lt;code&gt;git checkout -b&lt;/code&gt; as it creates both the worktree and the branch.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;`bash&lt;br&gt;
~/dev/kuro master&lt;br&gt;
❯ git worktree add ../.worktrees/kuro-fix-css -b fix-css&lt;br&gt;
Preparing worktree (new branch 'fix-css')&lt;br&gt;
HEAD is now at 9652578 &lt;a href="mailto:kuro@8.0.4"&gt;kuro@8.0.4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;~/dev/kuro master&lt;br&gt;
❯ cd ../.worktrees/kuro-fix-css &lt;/p&gt;

&lt;p&gt;~/dev/.worktrees/kuro-fix-css fix-css&lt;br&gt;
❯ git status&lt;br&gt;
On branch fix-css&lt;br&gt;
nothing to commit, working tree clean&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can read the &lt;a href="https://git-scm.com/docs/git-worktree" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for more details, but let me breakdown what I did.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I was in my &lt;code&gt;master&lt;/code&gt; branch with the latest changes&lt;/li&gt;
&lt;li&gt;I added  a &lt;strong&gt;linked working tree&lt;/strong&gt; called &lt;code&gt;fix-css&lt;/code&gt; which is coming from one directory up with in the  &lt;code&gt;.worktrees&lt;/code&gt; folder.  The &lt;code&gt;-b&lt;/code&gt; flag creates and checks out a new branch starting at the &lt;code&gt;HEAD&lt;/code&gt; when I run the command.

&lt;ul&gt;
&lt;li&gt;Opposed to &lt;strong&gt;linked working trees&lt;/strong&gt; we have the &lt;strong&gt;main working tree&lt;/strong&gt; which is the main repository directory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;I switch to my worktree directory.&lt;/li&gt;

&lt;li&gt;I'm now working on a newly created branch from &lt;code&gt;master&lt;/code&gt; called &lt;code&gt;fix-css&lt;/code&gt;
&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;I can then commit to this branch, but if for some reason I need to create another worktree from this my &lt;code&gt;master&lt;/code&gt; branch I can just do this:&lt;br&gt;
&lt;code&gt;&lt;/code&gt;`bash&lt;br&gt;
~/dev/.worktrees/kuro-fix-css fix-css*&lt;br&gt;
❯ git commit -m "some changes"&lt;br&gt;
[fix-css d04039b] some changes&lt;br&gt;
 1 file changed, 12 insertions(+), 12 deletions(-)&lt;/p&gt;

&lt;p&gt;~/dev/.worktrees/kuro-fix-css fix-css&lt;br&gt;
❯ git worktree add ../kuro-from-master master -b new-branch&lt;br&gt;
Preparing worktree (new branch 'new-branch')&lt;br&gt;
HEAD is now at 9652578 &lt;a href="mailto:kuro@8.0.4"&gt;kuro@8.0.4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;~/dev/.worktrees/kuro-fix-css fix-css&lt;br&gt;
❯ cd ../kuro-from-master &lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Breaking down the &lt;code&gt;worktree add&lt;/code&gt; command, we're adding a worktree in &lt;code&gt;../kuro-from-master&lt;/code&gt; pointing at our &lt;code&gt;master&lt;/code&gt; branch, and again the &lt;code&gt;-b&lt;/code&gt; flag creates and checks out the new branch in the worktree.&lt;br&gt;
At this point, if we change directory to any of the worktrees, or the original worktree (called &lt;strong&gt;main working tree&lt;/strong&gt;)  and we run &lt;code&gt;git branch --list&lt;/code&gt; we can see that we have 1 branch per worktree and all our regular branches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Behind the Scenes
&lt;/h2&gt;

&lt;p&gt;As I'm sure you have realized by now, we are basically creating a full copy of everything that's not targeted by our &lt;code&gt;.gitignore&lt;/code&gt; rules. Admitedly this is not ideal if you are working in a VM or in a more &lt;em&gt;limited&lt;/em&gt; machine. The codebase may not be that big, but remember you will most likely need to install dependencies everytime you initialize a worktree, unless  you don't have your depencies listed in &lt;code&gt;.gitignore&lt;/code&gt;, and if you do, please stop and take a moment to think about what you're doing with your life.&lt;/p&gt;

&lt;p&gt;There are two reasons we don't track our dependency folders with git&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;They can get pretty big, pretty fast.&lt;/li&gt;
&lt;li&gt;Everytime you update a dependency, you commit the compiled dependency changes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's why we have those fancy &lt;code&gt;install&lt;/code&gt; scripts.&lt;/p&gt;

&lt;p&gt;So for each worktree we're maintaining we have to allocate disk space for the project and all the dependencies. For a simple Javascript project, this goes to 400-600MB per worktree, so we should be mindful of deleting old worktrees with the &lt;code&gt;git worktree remove &amp;lt;worktree_path&amp;gt;&lt;/code&gt; command.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pros &amp;amp; Cons
&lt;/h2&gt;

&lt;p&gt;Let's quickly go over some pros &amp;amp; cons of the &lt;em&gt;vanilla&lt;/em&gt; worktree feature that comes out of the box when you install git compared to the workflow we all &lt;a href="https://git-scm.com/docs/git-checkout" rel="noopener noreferrer"&gt;know&lt;/a&gt; and &lt;a href="https://git-scm.com/docs/git-stash" rel="noopener noreferrer"&gt;love&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cons 👎
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You need to install all dependencies everytime you initialize a worktree

&lt;ul&gt;
&lt;li&gt;With &lt;code&gt;stash&lt;/code&gt; &amp;amp; &lt;code&gt;checkout&lt;/code&gt; you already have a dependency folder and only install the necessary updates.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;If not controlled, can take up alot of disk space.

&lt;ul&gt;
&lt;li&gt;With &lt;code&gt;stash&lt;/code&gt; &amp;amp; &lt;code&gt;checkout&lt;/code&gt; you only have on working directory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Unable to create/checkout branches checked out in worktrees

&lt;ul&gt;
&lt;li&gt;If you have a worktree pointing to a branch, you will not be able to &lt;code&gt;checkout&lt;/code&gt; that branch in the &lt;strong&gt;main working tree&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Easier to write &lt;code&gt;checkout &amp;lt;branch&amp;gt;&lt;/code&gt; than &lt;code&gt;cd &amp;lt;path_to_worktree&amp;gt;&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Yes. Read the following section where I introduce some tools that deal with this.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Pros 👍
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;You ONLY need to install dependencies whern you intialize a worktree.

&lt;ul&gt;
&lt;li&gt;Unless you're constantly pulling changes from other branches, you only need to install dependencies once.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You only need to run your environment scripts once.

&lt;ul&gt;
&lt;li&gt;If you work with databases, you need to reset or run migrations/backfills everytime you switch branches.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Worktrees allow you to just save your file and move contexts.&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;You avoid &lt;code&gt;WIP&lt;/code&gt; commits &amp;amp; endless stashes in your history &lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;You can run two instances of your code editor in different branches.

&lt;ul&gt;
&lt;li&gt;You can easily &lt;code&gt;cd&lt;/code&gt; into your worktree directories and open your code editor.&lt;/li&gt;
&lt;li&gt;To my knowledge, there's no way to do this with only the &lt;strong&gt;main working tree&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;From this list, you can see that the main downside to have in consideration is to mind how many worktrees exist at the same time in your machine because of the space they can take.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tools &amp;amp; Suggested Workflow
&lt;/h2&gt;

&lt;p&gt;My main issue with worktrees was always how different the command structure was from the all the  &lt;code&gt;git checkout&lt;/code&gt;  I was used to doing. If I was using it to create worktrees from the CLI, my first move would be to create two functions to ensure I had one centralized &lt;code&gt;.worktrees&lt;/code&gt; directory and that I could easily change into that directory. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
gwt () {&lt;br&gt;
  branch=${1}&lt;br&gt;
  git worktree add ~/dev/.worktrees/$branch -b $branch&lt;br&gt;
}&lt;br&gt;
gwtc () {&lt;br&gt;
  branch=${1}&lt;br&gt;
  cd ~/dev/.worktrees/$branch&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you're looking for simplicity and you don't have a big number of projects in your machine, this script will suffice for your CLI needs, however, I have been using two tools that make this process way more straightfoward while dealing with multiple projects.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitLens
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens" rel="noopener noreferrer"&gt;GitLens&lt;/a&gt; is a well known VSCode extension and the reason I stumbled across this feature. They have recently &lt;a href="https://www.gitkraken.com/blog/gitlens-12" rel="noopener noreferrer"&gt;released their take on bringing worktrees&lt;/a&gt; to VSCode and it makes creating worktrees and checking out respective branches really easy. You can select where you want the worktree stored (by default it uses the &lt;code&gt;../&amp;lt;project_name&amp;gt;.worktrees&lt;/code&gt; directory) and allows you to easily Open VSCode on and from any of the repo's worktrees.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git Worktree Switcher ⚡
&lt;/h3&gt;

&lt;p&gt;For the terminal, another great tool I've found is a little bit less main stream, is &lt;a href="https://github.com/yankeexe/git-worktree-switcher" rel="noopener noreferrer"&gt;Git worktree switcher! ⚡&lt;/a&gt; . This little script can be installed quite easily  and provides a nice way to switch between worktrees with fuzzy find support. In the example above, I can just do  &lt;code&gt;wt fix-css&lt;/code&gt;  and the script takes me to the &lt;code&gt;fix-css&lt;/code&gt; worktree directory,  and I can also jump back to the main working tree with &lt;code&gt;wt -&lt;/code&gt; . This also renders my second function completely useless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Workflow
&lt;/h3&gt;

&lt;p&gt;Regarding my workflow, I have the rule of only creating worktrees for either branches that are ready but are waiting for something (like a PR review or another feature being merged), to lend a hand to a colleague in another branch, or if I'm in the middle of something and a new bug required my immediate attention. Remember that for each worktree you create, you will take up disk space for the dependencies.&lt;br&gt;
When the need for a worktree arises I usually do the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I checkout the latest commit from the main branch&lt;/li&gt;
&lt;li&gt;In VS Code, I use GitLens to create the worktree (and the branch if it doesn't exist already, from the &lt;strong&gt;Source Control&lt;/strong&gt; sidepanel&lt;/li&gt;
&lt;/ul&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%2Fca853mh8whw0lxmiglfm.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%2Fca853mh8whw0lxmiglfm.png" alt="image.png" width="594" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I open the worktree with VSCode

&lt;ul&gt;
&lt;li&gt;GitLens allows you to open the worktree while keeping your main working tree window.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;On the CLI I do &lt;code&gt;wt &amp;lt;branch_name&amp;gt;&lt;/code&gt; and install the project dependencies.

&lt;ul&gt;
&lt;li&gt;In this workflow, the branch name and the worktree directory will be the same.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;If I need to switch back to the main worktree I can do &lt;code&gt;wt -&lt;/code&gt; on the comand line and open the code editor through GitLens (or by doing &lt;code&gt;code .&lt;/code&gt; on the CLI)&lt;/li&gt;

&lt;li&gt;Once I'm done and push my changes, I usually leave the worktree until the branch is merged.&lt;/li&gt;

&lt;li&gt;When I no longer need the worktree, I can remove it in VSCode on the &lt;strong&gt;Source Control&lt;/strong&gt; sidepanel.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;And this is pretty much it. By doing this, I have saved alot of time by not having to re-install depdencies or run migrations everytime I switch branches&lt;/p&gt;

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

&lt;p&gt;Hope you found this article interesting. I have been using worktrees for a very short time, so I bet there are way more integrations and tools for this amazing and hidden git feature. If you're aware of any other tools and hacks realted to worktrees, please leave a comment. &lt;/p&gt;

&lt;p&gt;Check out my &lt;a href="https://davidmorais.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; to learn about my most recent &lt;a href="https://epochrift.com" rel="noopener noreferrer"&gt;projects&lt;/a&gt; and follow me on &lt;a href="https://twitter.com/davidsmorais" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; 🤙&lt;/p&gt;

</description>
      <category>git</category>
      <category>github</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Devlog - Zero to First Trailer</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Thu, 03 Feb 2022 18:29:29 +0000</pubDate>
      <link>https://dev.to/davidmorais/devlog-zero-to-first-trailer-41ch</link>
      <guid>https://dev.to/davidmorais/devlog-zero-to-first-trailer-41ch</guid>
      <description>&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%2F0985ypj8rtp26g7e2pz6.gif" 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%2F0985ypj8rtp26g7e2pz6.gif" alt="Current Logo for Epoch Rift, animated" width="256" height="256"&gt;&lt;/a&gt;&lt;br&gt;
   A few months ago, I wrote &lt;a href="https://dsmorais.hashnode.dev/building-a-roguelike-game-with-typescript" rel="noopener noreferrer"&gt;a post&lt;/a&gt; sharing the project I have been working on and the tools I am using while making &lt;a href="//www.epochrift.com"&gt;Epoch Rift&lt;/a&gt; come to life.&lt;br&gt;
On that same post I have written something resembling a roadmap that I was guiding myself through. Today I want to share the progress I've made since aswel as share some of the diagrams that I've made back in May when I was thinking about starting to develop a game.&lt;/p&gt;

&lt;h2&gt;
  
  
  Game Design Document
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Core Mechanics
&lt;/h4&gt;

&lt;p&gt;So back in May, when I decided to embark on this journey, one of the most difficult tasks I had was to create something resembling a &lt;a href="https://en.wikipedia.org/wiki/Game_design_document" rel="noopener noreferrer"&gt;Game Design Document&lt;/a&gt;. Unline some of my &lt;a href="https://mdyna.netlify.com" rel="noopener noreferrer"&gt;previous projects&lt;/a&gt;, I wanted to have clear goals of I wanted to accomplish and limit my scope to what I had to do to make a basic game.&lt;/p&gt;

&lt;p&gt;I started by deciding the core mechaniccs. I have always loved platformers, in fact, one of the first games I have played was Prince of Persia for MS-DOS. Platformers can also be considered 'easy' to develop from a technical point of view, so I decided that my game would be a platformer ✅ What else ? &lt;/p&gt;

&lt;p&gt;Well, the last few years we've seen a re-surgence in 2D platformers, but with roguelike features, and I have been spent way too much time playing them. This left me with a whole lot of ideas for new mechanics and new ways to randomize the experience. I wanted that ✅.&lt;/p&gt;

&lt;p&gt;So we have a 2D platformer that would provide an unique player experience it would be played, by randomizing some elements in between runs.&lt;br&gt;
At this point, I honestly felt this was too 'vain' and needed to add something to keep the players playing and making them want to return to the game. I have always hated grinds, but easily fall prey to the feeling of reward of leveling up your character and gaining that +1 HP. I needed to have some sort of progression ✅.&lt;/p&gt;

&lt;p&gt;This is when I decided to find a good and &lt;strong&gt;free&lt;/strong&gt; diagraming and whiteboard application. Let's have the decency to reach the common understanding that having limits on how many boards/documents can be created is not &lt;strong&gt;free&lt;/strong&gt;. Flustered by my attempt, I picked &lt;del&gt;better Photoshop&lt;/del&gt; Affinity Photo and started to paste screenshots of some of the games I remembered that, one way or the other, checked the marks for what I wanted to accomplish. One of the games that featured alot was &lt;a href="https://store.steampowered.com/app/358290/Hocus_Pocus/" rel="noopener noreferrer"&gt;Hocus Pocus&lt;/a&gt;, released in 1994, and one of the games I played during my childhood with my father. The mechanics were pretty rudimentar. You had the &lt;code&gt;Arrow Keys&lt;/code&gt; for movement and looking up. &lt;code&gt;Alt&lt;/code&gt; for shooting little thunderbolts and &lt;code&gt;Ctrl&lt;/code&gt; to jump. That was it. This is where I took most inspiration from (look at the screenshots if it's pretty obvious). &lt;/p&gt;

&lt;p&gt;&lt;a href="https://epochrift.com" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt; is a 2D Platformer with roguelike features with a very simple mechanic. You can only play as spellcasters. No meleee attacks or abilities. You can play as multiple characters and learn a plethora of spells depending on the level you are on. You can play as multiple characters by first unlocking them, then buying them with some kind of in-game currency. To beat a level you need to find all the spheres in that level.&lt;br&gt;
Based on that, I drew up a diagram of some mechanics that I could implement, some randomizers I could introduce between levels or runs. This was my initial 'wishlist' 👇 &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%2F384hywm4emzfzjf1ejge.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%2F384hywm4emzfzjf1ejge.png" alt="Initial game core mechanics diagram" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the meantime I have dropped some mechanics, as I've quickly realized I'm working with Javascript on an engine with technical limitations and not that much documentation out there.&lt;br&gt;
Also featured on this document is this Game Flow I had in mind and unlike the previous, this diagram is still valid for the most part.&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%2F1ri9gts20jvcrnckndxj.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%2F1ri9gts20jvcrnckndxj.png" alt="Game flow diagram" width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I have decied to merge the &lt;code&gt;Character Selection Screen&lt;/code&gt; and the &lt;code&gt;Portal Room&lt;/code&gt; into a single &lt;code&gt;Player Hub&lt;/code&gt; in which you can upgrade your character, select your character for the run and select the Epoch you want to play.&lt;br&gt;
For future reference, an Epoch is a set of 3 levels + 1 Playable Boss, which becomes unlockable after you defeat it.&lt;br&gt;
This model also has the advantage of being scaleable. I can release content in sets of 3 levels + 1 boss or extra playable characters.&lt;/p&gt;

&lt;h4&gt;
  
  
  Branding &amp;amp; Name
&lt;/h4&gt;

&lt;p&gt;I feel like I have been neglecting this part of the process for quite a while. Let's start with the name:&lt;br&gt;
This project's name has been evolving until I actually started creating social media accounts and registering the domain.&lt;br&gt;
This projected started as &lt;strong&gt;Project Brass-Lizard&lt;/strong&gt;, then for a brief period of time, I thought about calling it Magus Rift.&lt;br&gt;
It was a game of wizards jumping through portals in time, so it sounded pretty intuitive. However, during the time I tried to go with this name, it always felt something was off. &lt;br&gt;
I landed in Epoch Rift, as the wizards are jumping through rifts in time, with the different ages of humanity being used as level settings. &lt;/p&gt;

&lt;p&gt;So this was the first branding itteration I had with Epoch Rift. It was the first 'official' logo with the lettering. &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%2F2ur5nz70nmtdogrr1fin.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%2F2ur5nz70nmtdogrr1fin.png" alt="Initial Branding for Epoch Rift" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Maybe I have a dirty mind, but that portal always looked &lt;em&gt;wrong&lt;/em&gt;, so for the time being, I've settled with this as the logo, which is the same sprite of the portal animation, upscaled. &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%2Fdbbqqu0dkgy7i63fwcbv.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%2Fdbbqqu0dkgy7i63fwcbv.png" alt="Current Logo for Epoch Rift, static" width="216" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I do admit I should consult with a graphic designer as this is something that can and should really be improved. Any suggestions or feedback are welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Process
&lt;/h2&gt;

&lt;p&gt;Since my last post I did not make much progress in terms of actual game content. I switched jobs in December and am still adjusting. It's a new codebase, new colleagues, new technologies and new routines. Add that to having to do house chores every day, and I admit that somedays I just do nothing.&lt;br&gt;
Since September, I added a new playable character, Morgana, with two new spells and the first status effect to the game, after you hit a monster with a Fireball, the monster will be burned.&lt;br&gt;
I have added the first map for the hub area where the player will respawn after a run.&lt;/p&gt;

&lt;p&gt;I also discovered the amazing &lt;a href="https://boscaceoil.net/" rel="noopener noreferrer"&gt;BoscaCeoil&lt;/a&gt; software and produced a few songs I think are good enough to be in the game.&lt;br&gt;
This all culminated in the trailer which was released and is feauturing in the landing page.&lt;/p&gt;

&lt;h3&gt;
  
  
  You can check out the trailer &lt;a href="https://github.com/Dark-Magic-Studios/epoch-rift-branding/raw/main/videos/trailer.mp4" rel="noopener noreferrer"&gt;here&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;There was also some progress on the marketing side. I designed and released the &lt;a href="https://www.epochrift.com" rel="noopener noreferrer"&gt;landing page&lt;/a&gt; with a mailing list (which has just recently changed their pricing plans and I have to make the change).&lt;br&gt;
I have also created a branding repository centralize and keep track of graphical assets. So if I need something I can just do a &lt;code&gt;git pull&lt;/code&gt; and have the latest assets.&lt;/p&gt;

&lt;p&gt;The biggest addition to the game was the portal system. In the trailer you still see the &lt;strong&gt;free&lt;/strong&gt; level.&lt;br&gt;
Above, I wrote that I wanted to randomize certain elements every run. A procedurally generated level, ideally, however let us be realistic, a pathfinding algorithm compatible with the toolset I'm using is not something I can do at my current skill, at least in the time I want to spend doing this. So here's how I'm making each run unique:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The levels now have rooms with walls blocking the way forward.&lt;/li&gt;
&lt;li&gt;Every rooms has two portals, one to go back and one to go to the next room.&lt;/li&gt;
&lt;li&gt;The portals are connected randomly each run and can lead you to a room with one or two spheres, monsters or rewareds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first level was not designed with the portal system in mind, so it only has 10 rooms, but the next level, which is already being developed should have many more rooms to provide a different experience every run.&lt;/p&gt;

&lt;p&gt;To sum up, here's what we currently have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 playable characters&lt;/li&gt;
&lt;li&gt;2 maps (1st level &amp;amp; player hub)&lt;/li&gt;
&lt;li&gt;5 spells&lt;/li&gt;
&lt;li&gt;2 monsters&lt;/li&gt;
&lt;li&gt;Portals System&lt;/li&gt;
&lt;li&gt;Landing Page&lt;/li&gt;
&lt;li&gt;Trailer&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I have already finishing designing the tileset for the next level and am at the level design stage and since I have created sort of 'templates' for new scenes, I expect the next level to be revealed within the next couple of weeks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Next Steps
&lt;/h2&gt;

&lt;p&gt;The demo release on &lt;a href="https://davidsmorais.itch.io/epoch-rift" rel="noopener noreferrer"&gt;itch.io&lt;/a&gt; is overdue, mostly because I did not account for the time it would take to work on the trailer and the landing page. So in the next few months I will be working on the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2nd &amp;amp; 3rd level

&lt;ul&gt;
&lt;li&gt;Visceral Hills &amp;amp; Great Pyramid of Nektanebo are the next two levels of the first Epoch.&lt;/li&gt;
&lt;li&gt;Visceral Hills on the insides of an ancient being. Meat and flehs are your surroundings.&lt;/li&gt;
&lt;li&gt;Great Pyramid of Nektanebo takes place inside a pyramid with an egyptian theme.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;1 boss encounter

&lt;ul&gt;
&lt;li&gt;To finish the first Epoch you must defeat the boss.&lt;/li&gt;
&lt;li&gt;After defeating the boss, it becomes an unlockable playable character.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;React UI

&lt;ul&gt;
&lt;li&gt;My current UI is messy and ugly. And due to its &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; based nature, Phaser is also not the best option to create UI. &lt;/li&gt;
&lt;li&gt;I intend to create some sort of bridge between the Phaser game and a React &lt;em&gt;overlay&lt;/em&gt; application which will be responsible for rendering the UI parts.

&lt;ul&gt;
&lt;li&gt;This includes dialogue, character upgrades, Game Over &amp;amp; Title Screens.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Steam Page Launch

&lt;ul&gt;
&lt;li&gt;For marketing purposes, before releasing the demo on itch, I really want to start gathering wishlist requests.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Wiki Launch

&lt;ul&gt;
&lt;li&gt;I'm currently using Obsidian to keep track of spells, characters &amp;amp; enemy documentation. I intend to create an engine to render my Obsidian vault as a Wiki website.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Release on itch.io

&lt;ul&gt;
&lt;li&gt;When all of the above is done, I need to create all the design assets inherent and copy content for an itch.io release.
## Future Projects
In the near future, I wish to create some open-source projects and distribute them to other indie game developers. This is also great to gain some visibility among the indie game developer scene.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;React UI Bridge

&lt;ul&gt;
&lt;li&gt;I still have not decided how I should call this, but this is basically a way to allow developers to use JSX to develop the UI of their phaser games.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Dialogue Manager

&lt;ul&gt;
&lt;li&gt;I want to have dialogue based a player's progress with a character. So if a player speaks twice to the same NPC, he will get diffent dialogues and advance in the game story.&lt;/li&gt;
&lt;li&gt;To simplify this, process I want to create a graphical interface to generate a &lt;code&gt;json&lt;/code&gt; that will be consumed by the React UI Bridge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Obsidian to Wiki

&lt;ul&gt;
&lt;li&gt;As said above, I intend to create a static website based on my markdown notes in my Obsidian vault. I have thought about using a CMS for this or even a &lt;a href="https://notion.so" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; page. But there's nothing like this in the open source scene, so it should be an interesting project to gain some visibility in an area more outside of the indie game dev circle.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;I have pretty much detailed the tools I'm using for this solo-development project in my &lt;a href="https://dsmorais.hashnode.dev/building-a-roguelike-game-with-typescript" rel="noopener noreferrer"&gt;previous post&lt;/a&gt;, still before we start I'm gonna share the updated list of my current stack.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Graphics

&lt;ul&gt;
&lt;li&gt;Pixel Art

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mapeditor.org/" rel="noopener noreferrer"&gt;Tiled&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aseprite.org/" rel="noopener noreferrer"&gt;Aseprite&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Other

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://affinity.serif.com/en-gb/photo/" rel="noopener noreferrer"&gt;Affinity Photo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://figma.com" rel="noopener noreferrer"&gt;Figma&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Code

&lt;ul&gt;
&lt;li&gt;VsCode

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://phaser.io/" rel="noopener noreferrer"&gt;Phaser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.typescriptlang.org/" rel="noopener noreferrer"&gt;Typescript&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Hosting &amp;amp; Source Control

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gitkraken.com/" rel="noopener noreferrer"&gt;Gitkraken&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Music Production

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://boscaceoil.net/" rel="noopener noreferrer"&gt;Bosca Ceoil&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Project Management &amp;amp; Documentation

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://excalidraw.com/" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://obsidian.md" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ora.pm" rel="noopener noreferrer"&gt;Ora&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;p&gt;I know I still need to invest in my branding efforts and improve on that front, but for now I really feel like I have spent enough time on this. &lt;br&gt;
I expect the next few months to be more smooth and provide much more in-game content, and hopefully, the next devlog will be to tell you about our demo release 🚀&lt;br&gt;
Hope you liked to learn more about my project, &lt;a href="https://epochrift.com" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt;. Do you have any questions or suggestions ? Feel free to drop a coment or contact me via &lt;a href="https://dsmorais.com" rel="noopener noreferrer"&gt;my website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/epoch_rift" rel="noopener noreferrer"&gt;Follow Epoch Rift&lt;/a&gt;&lt;br&gt;
&lt;a href="https://twitter.com/davidsmorais" rel="noopener noreferrer"&gt;Follow David Morais&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>phaser</category>
    </item>
    <item>
      <title>My 2022 Productivity Suite</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Thu, 13 Jan 2022 20:47:57 +0000</pubDate>
      <link>https://dev.to/davidmorais/my-2022-productivy-suite-1enb</link>
      <guid>https://dev.to/davidmorais/my-2022-productivy-suite-1enb</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Some of you may have read my &lt;a href="https://dsmorais.com/#/./post?slug=top-2021-productivity-apps" rel="noopener noreferrer"&gt;article from last year&lt;/a&gt;  about my Productivity Apps for 2021. It's always a good read, especially if you're like me, looking for a new applications all the time.  That list is slightly outdated as I have dropped some of the applications there and have found better options for them. In this post, besides listing the new applications I am using, I will also talk a little bit my daily workflow and how I combine the &lt;del&gt;perhaps too&lt;/del&gt;  many apps I am using.&lt;/p&gt;

&lt;p&gt;Let's start with the apps that have replaced last year's picks. &lt;/p&gt;

&lt;h2&gt;
  
  
  Apps
&lt;/h2&gt;

&lt;p&gt;Before we head into the list itself, a disclaimer: These apps are supposed to &lt;strong&gt;help&lt;/strong&gt; you be more productive. None of the following will do your work on its own, you &lt;strong&gt;must&lt;/strong&gt; make use of them. Therefore, allow me to explain the factors that will make me actually &lt;strong&gt;want&lt;/strong&gt; to use an app:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cross Platform Compatibility 💻

&lt;ul&gt;
&lt;li&gt;I use Windows, Linux, Chrome OS and Android, so being able to use the same app across all my devices is a &lt;strong&gt;MUST&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Offline Support 📵

&lt;ul&gt;
&lt;li&gt;I did not think I needed it, until I did.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Dark Theme 🕶️

&lt;ul&gt;
&lt;li&gt;Because I do not like wear sunglasses when looking at my screens.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Desktop Client 📦

&lt;ul&gt;
&lt;li&gt;For the desktop apps, I prefer to have them as a separate window instead of something attached to my browser.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Price 💸

&lt;ul&gt;
&lt;li&gt;Because I spend too much money on crypto without any meaningful return.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Password Manager
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://bitwarden.com/" rel="noopener noreferrer"&gt;Bitwarden&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Linux, Chrome OS, Browser, Windows, macOS &amp;amp; iOS&lt;/li&gt;
&lt;li&gt;📵Offline Support&lt;/li&gt;
&lt;li&gt;🕶️ Grey/Nord Theme&lt;/li&gt;
&lt;li&gt;📦 Desktop Client &amp;amp; Browser Extension&lt;/li&gt;
&lt;li&gt;💸 Free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bitwarden is not new to me. I have used it before switching to &lt;a href="https://myki.com/" rel="noopener noreferrer"&gt;MYKI&lt;/a&gt; last year. However, MYKI broke completely on my PopOS machine and after speaking with the devs via Reddit, I was not getting anywhere and kind of needed my credentials.&lt;br&gt;
Looking at the &lt;strong&gt;Free&lt;/strong&gt; options in the Password Management department, there aren't many options out there. Bitwarden is safe, open source, and cross platform. &lt;br&gt;
I decided to give it another try and I must say I was positively surprised with the improvements I have found since I used the app for the last time. &lt;br&gt;
One of my biggest complaints, was that unlike MYKI, Bitwarden didn't prompt you to Save passwords unless you had the client open &amp;amp; unlocked, and I honestly don't know if they already had the Vault Timeout setting back then, but by setting my Vault to lock on my computer restart solved that issue. &lt;br&gt;
Two great additions to Bitwarden were the dark/nord theme which I can't remember being there back when I first tried it.&lt;br&gt;
The UI is not the prettiest, in my opinion, but it is very intuitive and fast.&lt;br&gt;
Overall, it does everything you'd expect from a password manager: you can save passwords, credit cards, tokens, just about anything really... Also has this neat feature that allows you to send self-destructible notes to your friends and colleagues. Specially useful if you want to onboard someone on a project. &lt;br&gt;
Another cool addition was the Windows Hello integration which I'm 99% sure was not there in the past.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Management
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://ora.pm/" rel="noopener noreferrer"&gt;Ora.pm&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Linux, Browser, Windows, Android &amp;amp; macOS&lt;/li&gt;
&lt;li&gt;📵Offline Support&lt;/li&gt;
&lt;li&gt;🕶️ One of the most beautiful apps on the list&lt;/li&gt;
&lt;li&gt;📦 Desktop Client&lt;/li&gt;
&lt;li&gt;💸 Free/Premium&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like the application above, Ora.pm is not new to me. In fact, I have used it pretty much throughout the whole development of &lt;a href="https://www.mdyna.netlify.com" rel="noopener noreferrer"&gt;MDyna&lt;/a&gt;.&lt;br&gt;
Last year, I gave &lt;a href="https://www.jetbrains.com/youtrack/" rel="noopener noreferrer"&gt;YouTrack&lt;/a&gt; a try and it does a solid job as a JIRA alternative. I'd use it as a JIRA alternative. But that's not what I'm looking for, now is it ? Besides only having a browser version, no offline support and a terrible theme, I wan't something a little lighter to manage my side projects. So after building my &lt;a href="https://dsmorais.com" rel="noopener noreferrer"&gt;webside&lt;/a&gt; using YouTrack, I decided it was time to look for an alternative. And oh boy... did I look.&lt;br&gt;
I think this is one of the thoughest applications of my stack because of the &lt;em&gt;crazy&lt;/em&gt; amount of PM apps birthed every day, each with 5 or so different pricing plans. &lt;br&gt;
I wanted an application which filled the above criteria, but also something that would not limit me according to their pricing plans. I did a &lt;a href="https://docs.google.com/spreadsheets/d/11lpZK7ULIVfRwWyJxMDjczPKIPmGLZXrRWPy66Qbu8g/edit?usp=sharing" rel="noopener noreferrer"&gt;comparison chart&lt;/a&gt; of a few select PM tools, combining price with the features * &lt;strong&gt;I&lt;/strong&gt; valued*, and Ora.pm came up second, after YouTrack. And that is because YouTrack is 100% free, so their price/feature is always going to be 0.&lt;br&gt;
&lt;a href="https://ora.pm/" rel="noopener noreferrer"&gt;Ora.pm&lt;/a&gt; is prehaps the lesser known application on this whole list, unfairly so. It's feature rich, affordable, and their themes are stunning.&lt;br&gt;
Full disclosure, there are a few issues with it, for example, I've found that the GCal integration is better left unused if you like to move deadlines around in a Gantt Chart.&lt;br&gt;
However, you get Sprints, Burndown Charts, Unlimited Tasks, Custom Fields &amp;amp; Milestones for &lt;strong&gt;free&lt;/strong&gt;. I ended up paying for a plan because developing I wanted tasks relationships for developing &lt;a href="https://epochrift.com" rel="noopener noreferrer"&gt;Epoch Rift&lt;/a&gt; and it's totally worth my 6$/month.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://obsidian.md/" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Linux, Windows, Android, macOS, iOS&lt;/li&gt;
&lt;li&gt;📵Offline Support&lt;/li&gt;
&lt;li&gt;🕶️ Plenty of themes to choose from, super easy to customize them with CSS&lt;/li&gt;
&lt;li&gt;📦 It's a desktop app&lt;/li&gt;
&lt;li&gt;💸 Free (but...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first debutant in this list.&lt;br&gt;
Obsidian has been one of the most pleasant surprises I came across last year. For those who don't know, it's a Markdown notes application and replaces &lt;a href="https://notion.so" rel="noopener noreferrer"&gt;Notion&lt;/a&gt; on this category. Last year I went on vacation to a rural area and my connection was either painfully slow or non-existant at all, and since I don't keep Notion open all the time, I had no way to access it.&lt;br&gt;
If the best part of Obsidian are the &lt;a href="https://obsidian.md/plugins" rel="noopener noreferrer"&gt;Plugins&lt;/a&gt; which you can install right out of the client, and there are 2 or 3 I would consider 'core' to make your overall experience better. The second best part is that it works offline across your devices, with a minor caveat... Sync is premium (and a bit pricey in my opinion). There are workarounds like using a cloud provider like OneDrive or Dropbox, I'm using the git plugin which basically backs up my vault to a Github private repo. While it's not practical to use on mobile, I will go into detail later how I deal with it.&lt;br&gt;
If you don't know Obsidian I suggest you check it out, I've migrated everything from Notion and despite the fact that it took me about 4 hours to do so, I regret nothing. The writing experience is pleasant, there are hundreds of themes, which can be easily customizable if you know basic CSS, and all the data is private and owned by you, unlike other alternatives. The downside is missing the sync on mobile, but there are apps which may force your cloud folders to update on Android (and I believe in iOS aswel).&lt;/p&gt;

&lt;h3&gt;
  
  
  Browser
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://brave.com/" rel="noopener noreferrer"&gt;Brave&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Linux, Windows, Android, macOS &amp;amp; iOS&lt;/li&gt;
&lt;li&gt;🕶️ Chrome Theme Store&lt;/li&gt;
&lt;li&gt;💸 Free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, it seems I have spent a year without changing browsers 🎉&lt;br&gt;
Brave is the first app that returns to this list, the privacy-focused browser is still my main (and in most cases, only) browser.&lt;br&gt;
I think it is closely tied with Edge as the fastest browser I have used on macOS, Windows and Android. The last time I checked Edge, it still did not support account Sync, so I ditched it for Brave and haven't looked back since.&lt;br&gt;
Ads are blocked by default and it’s easy to make changes to the shields when they inadvertently block things that you want to see. Brave’s privacy focus is a huge selling point especially for people who follow tech and have noticed just how creepy the predictive algorithms are getting. &lt;br&gt;
And the best part is that you can actually earn Brave Attention Tokens -BAT- from watching ads, which on its own &lt;a href="https://coinmarketcap.com/currencies/basic-attention-token/" rel="noopener noreferrer"&gt;has valued around 300%&lt;/a&gt; during the last year. We are still at the begining of what Brave promises to be a revolution in the way online advertising works, competing directly with Google and Facebook's model.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code Editor
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Our Lord and Savior&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Linux, Windows, macOS &amp;amp; Browser (preview)&lt;/li&gt;
&lt;li&gt;🕶️ I'm using &lt;a href="https://marketplace.visualstudio.com/items?itemName=icao.electron-vue" rel="noopener noreferrer"&gt;this theme&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📦 Electron Based 😔&lt;/li&gt;
&lt;li&gt;💸 Free
I have been using VSCode since 2018, when I started working as a front end developer. Back then, I was using Atom and when I made the switch, the powerful extension ecosystem had me hooked to it from the start.
Over the last year they've added the Settings Sync with your Github account, which makes me rely on one less extension. I've also gotten into the &lt;a href="https://copilot.github.com/" rel="noopener noreferrer"&gt;Github Copilot &lt;/a&gt; beta and I completely recommend anyone who's reading this to do so aswell as the experience at first feels like your machine is under a dark voodoo magic spell.
I've written &lt;a href="https://dsmorais.com/#/./post?slug=my-vs-code-setup-and-less-known-extensions-1" rel="noopener noreferrer"&gt;a post&lt;/a&gt; with my settings &amp;amp; best extensions a while ago, I recommend giving it a read if you're looking to customize or improve your VSCode experience&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  To-Do Lists
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://keep.google.com/u/0/" rel="noopener noreferrer"&gt;Google Keep&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Browser, Android &amp;amp; iOS&lt;/li&gt;
&lt;li&gt;📵Offline Support&lt;/li&gt;
&lt;li&gt;🕶️ Broken dark theme&lt;/li&gt;
&lt;li&gt;📦 No desktop client, only mobile apps&lt;/li&gt;
&lt;li&gt;💸 Free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion, this is one of the most neglected apps, with great potential in the Google Cloud ecosystem.&lt;br&gt;
There are no desktop clients for this and the API is enterprise-only.&lt;br&gt;
The dark theme looks like they just added &lt;code&gt;filter: invert(-1)&lt;/code&gt; on the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; of the HTML.&lt;br&gt;
There aren't many reasons to check out Google Keep in 2022, however I still use it, mostly on my phone, for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Acts as inbox for quick notes &amp;amp; lists or for when Obsidian isn't synced on my Android device&lt;/li&gt;
&lt;li&gt;Google Assistant integration for my shopping list, which I share with my girlfriend's Gmail account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://todo.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft ToDo&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Browser, macOS, Windows, Android &amp;amp; iOS&lt;/li&gt;
&lt;li&gt;📵Offline Support&lt;/li&gt;
&lt;li&gt;🕶️ Beautiful theme, except for the browser.&lt;/li&gt;
&lt;li&gt;📦 Linux users are limited to the browser version&lt;/li&gt;
&lt;li&gt;💸 Free&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one beautiful application, if you run it on Windows, macOS or on your mobile. It's the result of Microsoft absorbing Wunderlist and I use it for all my non virtual ToDos.  For free, you get lists, repeatable tasks and reminders. Its main feature is the 'My Day' list which suggests you task to plan your day.&lt;br&gt;
The only downsides are the fact that you need a Microsoft account, the lack of integration with the Google Assistant (which would make me ditch Keep) and the fact that there's no Linux application.&lt;br&gt;
There's a browser app in your outlook panel, and I could live with that, but unfortunately, there is no dark theme on the browser app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Charts &amp;amp; Whiteboards
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://excalidraw.com/" rel="noopener noreferrer"&gt;Excalidraw&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Browser&lt;/li&gt;
&lt;li&gt;📵Offline Support&lt;/li&gt;
&lt;li&gt;🕶️ Night Mode works well&lt;/li&gt;
&lt;li&gt;📦 Browser, but I'm using the Obsidian Plugin&lt;/li&gt;
&lt;li&gt;💸 Free + Premium&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had the need of a charts app when I was developing a small prototype for a code challenge and wanted to present a diagram of what I was going to do.&lt;br&gt;
Like project management tools, diagrams apps seem to be all the rage these days, and they spawn like rabbits in the spring. &lt;br&gt;
The biggest issues with most of them is that they actually charge you for having more than &lt;em&gt;n&lt;/em&gt; boards. Which I understand, but sucks. I won't use a whiteboarding tool that often, however, I don't want to limit the boards I can have saved. Excalidraw also does this as a monetizing strategy. However, they allow you to export your charts. I can save all my diagrams in my drive and open them later.&lt;br&gt;
I won't like and tell you that Excalidraw was my first choice, however, they have an Obsidian plugin which integrates seamlessly and keeps my diagrams in my Obsidian repo for later. &lt;br&gt;
They also have a huge shapes library which I totally recomend digging upon.&lt;/p&gt;

&lt;h3&gt;
  
  
  Git GUI
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.gitkraken.com/" rel="noopener noreferrer"&gt;Gitkraken&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;💻 Windows, Linux, macOS&lt;/li&gt;
&lt;li&gt;🕶️ Beautiful theme, except for the browser.&lt;/li&gt;
&lt;li&gt;💸 Free + Premium (4.95€/mo for private repos)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like VSCode, Gitkraken has been with my for the best part of my career.&lt;br&gt;
In my first job, I had no git knowledge besides checkout, push, pull &amp;amp; commit, and was working with a senior developer who insisted on using a rebase workflow. For some time, I actually became known as 'rebases' on the co-working space due to the (admittedly too many) times my senior would re-explain the rebase workflow to me. Then I found GitKraken and really got the hang of wtf was going on in a git repository. Now I can do almost anything with a git repo. I don't buy into that CLI vs GUI war. CLI is fast, and knowing the commands will save you a shit load of time and may prevent you from looking like a fool in conversations, but when you have a complicated git workflow/large teams/branch-palooza 🎉, actually looking at the branches state in a clean neat way will save you hours.&lt;br&gt;
They've just launched their CLI which allows you to control Gitkraken from the comand line while watching everything happening in the branch interfance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bonus
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://anytype.io/" rel="noopener noreferrer"&gt;Anytype.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anytype is built on web3 and is an open source Notion alternative. I got a chance to get in the Early Alpha, and I honestly thought it was not polished enough to replace Obsidian. However, I have high hopes for this one. &lt;br&gt;
It's 100% free, works on all platforms and syncs your data across devices. Theming is not the best, but it has a dark theme. &lt;br&gt;
It's like Notion but not quite, in the sense that everyone is can have a different type (not just notes/databases) and each type has different properties. Which is great for templating and whatnot.&lt;br&gt;
I'm still holding for the application to be a bit more robust, but I can't say I wouldn't choose this over Obsidian if I had gotten into the Early Access earlier. So sign up for the Early Access or subscribe to their newsletter because I honestly believe this one is going places 🚀&lt;/p&gt;

&lt;p&gt;This is it for now. Feel free to share what apps you are currently using or if you actually checked out and liked one of the apps I've suggested here 🙂&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tooling</category>
      <category>opensource</category>
      <category>git</category>
    </item>
    <item>
      <title>Building a Roguelike Game with TypeScript</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Sat, 25 Sep 2021 19:10:43 +0000</pubDate>
      <link>https://dev.to/davidmorais/building-a-roguelike-game-with-typescript-105l</link>
      <guid>https://dev.to/davidmorais/building-a-roguelike-game-with-typescript-105l</guid>
      <description>&lt;p&gt;In this article I'm writing about the experience of building a game as someone with a front-end background.&lt;br&gt;
Epoch Rift is a 2d platformer with roguelike features I'm currently building. It's unique feature is that you can play only as wizards. No swords allowed ⛔&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%2F6hh3ov3pv6gskqzro0cp.gif" 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%2F6hh3ov3pv6gskqzro0cp.gif" alt="lelve_resize.gif" width="200" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;Hey everyone 👋&lt;br&gt;
It's been a &lt;strong&gt;while&lt;/strong&gt; since I've posted something. This is mostly due to the fact that I'm currently building &lt;a href="https://twitter.com/epoch_rift" rel="noopener noreferrer"&gt;a game&lt;/a&gt;. Working a full time job and on side projects, I've almost no time left to write posts, which is kind of a side project itself.&lt;/p&gt;

&lt;p&gt;I've built some small games in the 2000's with the &lt;a href="http://www.byond.com/games/" rel="noopener noreferrer"&gt;BYOND&lt;/a&gt; engine. I had no real programming foundations, yet I had a basic understand what terms like &lt;code&gt;mobs&lt;/code&gt;, &lt;code&gt;procs&lt;/code&gt; and &lt;code&gt;var&lt;/code&gt; were. Fast forward 15 years and I'm a senior front end developer who's built several applications using Javascript and I've been wanting to build an original game ever since I got into this career. I also know other languages like Ruby and Python, which are not really good options for developing games. But then again, Javascript also didn't seem to be a viable option for building a game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Battle of the Engines
&lt;/h3&gt;

&lt;p&gt;Having read multiple articles comparing the many different game engines, one thing was guaranteed: Creating a game with Javascript did not seem possible, at least without an huge amount of boilerplate work. I would have to &lt;em&gt;settle&lt;/em&gt; for a typed language like C#. When looking for an engine, I have to take multiple factors in consideration, one of them is the size and activity of the community. Having no 'real' experience with game development, I better have everything I need on the first page of Google, minimum. Imagine building an application with a framework with &lt;strong&gt;zero&lt;/strong&gt; StackOverflow posts. Daunting, isn't it ? &lt;/p&gt;

&lt;p&gt;So I'm still decided to learn Unity and do a project with it. However, my C# knowledge is close to &lt;code&gt;nil&lt;/code&gt; 😛. &lt;br&gt;
I've known about &lt;a href="https://phaser.io/" rel="noopener noreferrer"&gt;Phaser&lt;/a&gt; for a few years now, and stayed away from it for multiple reasons, the main one being that there really isn't a big community, compared to other engines like &lt;a href="https://unity.com/" rel="noopener noreferrer"&gt;Unity&lt;/a&gt; or &lt;a href="https://godotengine.org/" rel="noopener noreferrer"&gt;Godot&lt;/a&gt;. But currently, it's almost fully compatible with Typescript, which in my head, is acting like a stepping stone to C#. So I went it it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Phaser + Typescript
&lt;/h3&gt;

&lt;p&gt;Phaser has its caveats, but it has a lot of tutorials. In fact, most stuff I've implemented, I used their tutorial examples as a template. Their API documentation is a bit fuzzy and not everything will be on the first page you hit.&lt;br&gt;
After dwelling through GitHub, I forked a nice &lt;a href="https://github.com/fubira/cordova-phaser-typescript-template" rel="noopener noreferrer"&gt;Cordova Phaser Template with Typescript&lt;/a&gt; which has some cool feature out of the box, mainly a BGM player (with &lt;a href="https://howlerjs.com/" rel="noopener noreferrer"&gt;Howler.js&lt;/a&gt;), Firebase integration and mobile platform support with &lt;a href="https://cordova.apache.org/" rel="noopener noreferrer"&gt;Cordova&lt;/a&gt; on top of a webpack config with a dev server, and of course, Typescript support.&lt;/p&gt;

&lt;p&gt;I still develop much faster using plain ol' JavaScript, so I took some time to figure the proper TS typings, and still have &lt;em&gt;one or two&lt;/em&gt; &lt;code&gt;any&lt;/code&gt; types &lt;em&gt;lost&lt;/em&gt; in the codebase. Still, for custom logic, it makes perfect sense, at it feels very intuitive having autocomplete when updating scenes or sprites.&lt;br&gt;
Eventually, I moved all my "template" classes into a &lt;code&gt;Base&lt;/code&gt; class which would be extended to the different sub types. For example, instead of duplicating most of the code to create two different enemies, I've merged the common parts into a &lt;code&gt;BaseEnemy&lt;/code&gt; which I then extend enemies with different behaviors.&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%2F8qxcpdkl3z2frrtjgd1a.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%2F8qxcpdkl3z2frrtjgd1a.png" alt="game-timeline.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see the progress I've been making ever since I've successfully added a map and a moving sprite. As someone who's been mainly developing apps with React, I had  immutability in mind, so developing with mutable objects and updatable classes, it's been complete paradigm shift.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tools
&lt;/h3&gt;

&lt;p&gt;As I said in the beginning, I work a full time job and am currently helping my girlfriend start her own project, so I don't have tons of spare time to build games. I have to say that the fact that I've made this progress in these 3 months, is because I've been using some of these tools which help me in someway or another to stay organized and be more productive.&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%2F9kx61i4rigex1cpewfa0.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%2F9kx61i4rigex1cpewfa0.png" alt="apps.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Project Management
&lt;/h4&gt;

&lt;p&gt;I've always used project management tools for my solo projects. For this project I changed over from &lt;a href="https://ora.pm" rel="noopener noreferrer"&gt;Ora&lt;/a&gt; to &lt;a href="https://clickup.com/" rel="noopener noreferrer"&gt;ClickUp&lt;/a&gt; last month. For managing what needs to be done, defining a roadmap and managing my progress, ClickUp's free tier does the trick. I've cooperated with two social media managers so far and a project management tool to centralize your game documents is essential.&lt;br&gt;
So far I've only contracted the services of two social media managers, but I will eventually need to at least 1 graphic designer. I've been using &lt;a href="http://obsidian.md/" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt; to store Game Design Documents, spreadsheets and diagrams, but I will eventually move some of this to ClickUp in order to make task creation and onboarding more efficient.&lt;/p&gt;

&lt;h4&gt;
  
  
  Code &amp;amp; Source Control
&lt;/h4&gt;

&lt;p&gt;Despite the fact that I'm going solo on this one, I've also been using &lt;a href="https://www.gitkraken.com/" rel="noopener noreferrer"&gt;Gitkraken&lt;/a&gt; with WSLg as a Git GUI to help me visualize what changes I did in the codebase. For example, I can easily find a specific commit which introduced a bug in the codebase and can easily revert it with the built-in code editor. It's one of the products I definitely recommend you get for everything you do that involves a git repository.&lt;br&gt;
As per usual, I'm using &lt;a href="https://code.visualstudio.com/" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt; which goes on steroids when using Typescript. It's been a really smooth experience. I've installed some TS extensions, and I was ready to go. You can check &lt;a href="https://dsmorais.hashnode.dev/my-vs-code-setup-and-less-known-extensions-1" rel="noopener noreferrer"&gt;my last post&lt;/a&gt; to find which extensions I'm currently using.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pixel Art
&lt;/h4&gt;

&lt;p&gt;I bought and have been using &lt;a href="https://www.aseprite.org/" rel="noopener noreferrer"&gt;Aseprite&lt;/a&gt; to draw almost all of my pixel art. I'm not the best sketch artist out there, by any means, but Aseprite has plenty of tools you don't find in software like Photoshop or Affinity Photo. &lt;br&gt;
The best thing, is that you can generate spritesheets and tag your animations in Aseprite and Phaser already has a &lt;code&gt;createFromAseprite&lt;/code&gt; function which automatically maps the animations in your spritesheet.&lt;br&gt;
One thing I've learned the hard way is that if you have two animations with the same name from different sprites, you will have conflicts.&lt;br&gt;
You have multiple examples on Phaser's website on how to export and load a spritesheet from Aseprite.&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%2Fhtf3kppfutmkz592dihq.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%2Fhtf3kppfutmkz592dihq.png" alt="aseprite.png" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Mapping
&lt;/h4&gt;

&lt;p&gt;This one is &lt;strong&gt;free&lt;/strong&gt; and open source. &lt;a href="https://www.mapeditor.org/" rel="noopener noreferrer"&gt;Tiled &lt;/a&gt; alows you to create your level maps with a graphical interface. One of my inspirations was built with Tiled and I had already heard about it. You can import your tilesets (created in Aseprite), have multiple tile layers and object layers, which are used to pin enemy spawns or chests.&lt;br&gt;
Again, the most defining feature that made me use this was Phaser's out of the box support for Tiled maps. &lt;strong&gt;You need to embed your tileset&lt;/strong&gt; in your tiled export, and then simply preload the &lt;code&gt;json&lt;/code&gt; file, and you can load it in Phaser by doing &lt;code&gt;this.make.tilemap({ key: &amp;lt;map_key&amp;gt; });&lt;/code&gt; on your scene. &lt;br&gt;
Also, like with Aseprite, there's tons of examples for loading a Tiled map and its layers onto your Phaser game.&lt;/p&gt;

&lt;h3&gt;
  
  
  Roadmap
&lt;/h3&gt;

&lt;p&gt;So I've been at it for the last months, what have I got to show ?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The main hub which you will pass through when starting a run.&lt;/li&gt;
&lt;li&gt;The first level which you can clear by collecting 10 spheres spread around the map.&lt;/li&gt;
&lt;li&gt;Three spells with slightly different effects.&lt;/li&gt;
&lt;li&gt;Gated areas,&lt;/li&gt;
&lt;li&gt;A lighting and particles system which renders certain effects based on Tiled's object layers (for example, fire braziers).&lt;/li&gt;
&lt;li&gt;A very rudimentar HUD which displays your heatlh, your collectibles and the cooldown of your spells.&lt;/li&gt;
&lt;li&gt;Essential physics mechanics (dash, jump, collisions, enemy flight...)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the next few months, I want to have the following done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new level with a new set of enemies.&lt;/li&gt;
&lt;li&gt;Another playable character with different stats.&lt;/li&gt;
&lt;li&gt;More spells that the player can use.&lt;/li&gt;
&lt;li&gt;A boss fight.&lt;/li&gt;
&lt;li&gt;New physics mechanics like elevator turfs and level portals.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I feel like  require a lot of pixel art work as most of the groundwork for the game is built. There are alot of challenges I see ahead, and plenty more I can't even fathom. So stay tuned for when I post the next devlog in three to six months 😛&lt;/p&gt;

&lt;p&gt;If you liked this project, follow Epoch Rift on &lt;a href="https://twitter.com/epoch_rift" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; as I'm posting screenshots and sharing development updates much more often there.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
    <item>
      <title>My VS Code Setup (and less known extensions)</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Fri, 30 Apr 2021 17:06:26 +0000</pubDate>
      <link>https://dev.to/davidmorais/my-vs-code-setup-and-less-known-extensions-3elp</link>
      <guid>https://dev.to/davidmorais/my-vs-code-setup-and-less-known-extensions-3elp</guid>
      <description>&lt;p&gt;Hey hey 👋&lt;/p&gt;

&lt;p&gt;You may look at the title and sarcastically say "Yay, another VS Code extensions list", at least I would. The fact is that I end up opening said articles and realize that most times they share 8 out of 10 extensions between them all. I use most of them, so I'm not going to go into detail about those that are most featured in articles, instead I'll try to analyze the lesser known and most useful extensions I'm using.  I've split the extension list into categories so you can easily find whatever you're looking for here, and at the end of the article you can find the full list of extensions I'm using.&lt;/p&gt;

&lt;h1&gt;
  
  
  Our Lord and Savior
&lt;/h1&gt;

&lt;p&gt;Let's start with the editor itself. VS Code is a code editor made by Microsoft. It's mainly powered by the open source &lt;a href="https://microsoft.github.io/monaco-editor/" rel="noopener noreferrer"&gt;Monaco Editor&lt;/a&gt;, extensions and the extensive settings API. It's also pretty lightweight for all that it offers which is why I changed from Atom and have never stopped preaching about &lt;strong&gt;our lord and savior VS Code.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By using an extension called &lt;a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync" rel="noopener noreferrer"&gt;Settings Sync&lt;/a&gt;, I've kept about the same configuration throughout the years. Recently Microsoft added a built-in Sync feature which seems to work even better. Sometimes I clean up my extensions, change the color and icon theme and install a few new jewels, hence why I'm currently rocking 59 extensions.&lt;/p&gt;

&lt;p&gt;This is my VS Code setup at the moment 👇&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%2Fbyzpmwohvweyc25zqhgb.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%2Fbyzpmwohvweyc25zqhgb.png" alt="My VS Code Setup" width="800" height="650"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some main changes from the initial layout include the &lt;strong&gt;sidebar on the right side, the activity bar, theme&lt;/strong&gt; and &lt;strong&gt;sidebar items.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Starting with the theme, I'm currently using these extensions to achieve this look:&lt;/p&gt;

&lt;h2&gt;
  
  
  Theming &amp;amp; Layout
&lt;/h2&gt;

&lt;p&gt;Theme: &lt;a href="https://marketplace.visualstudio.com/items?itemName=icao.electron-vue" rel="noopener noreferrer"&gt;Electron vue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sidebar Icons: &lt;a href="https://marketplace.visualstudio.com/items?itemName=antfu.icons-carbon" rel="noopener noreferrer"&gt;Carbon Product Icons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opacity (Linux &amp;amp; Windows only): &lt;a href="https://marketplace.visualstudio.com/items?itemName=s-nlf-fh.glassit" rel="noopener noreferrer"&gt;GlassIt-VSC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Folder and File Icons: &lt;a href="https://github.com/moxer-theme/moxer-icons-code" rel="noopener noreferrer"&gt;Moxer Icons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Font: &lt;a href="https://github.com/tonsky/FiraCode" rel="noopener noreferrer"&gt;FiraCode&lt;/a&gt; (w/ ligatures)&lt;/p&gt;

&lt;p&gt;For the sidebar on the right side and other customizations that are available out of the box, you can add this to your &lt;code&gt;settings.json&lt;/code&gt; (You can open your settings by pressing Ctrl + P and selecting 'Open Settings (JSON)'.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workbench.editor.tabCloseButton&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;off&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// I was accidentally closing tabs too often, so I removed the close button&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;workbench.sideBar.location&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;right&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Pretty straightfoward**&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.fontFamily&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'Fira Code', 'Droid Sans Mono', 'monospace', monospace, 'Droid Sans Fallback'&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Fira Code is an open source ligature font that I strongly recommend you install&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.fontLigatures&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Enable font ligatures for Fira Code&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.formatOnPaste&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Formats your pasted content&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.minimap.enabled&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Enables the file minimap&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.minimap.maxColumn&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Minimap char count&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.minimap.renderCharacters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// You can render actual characters instead of the color blocks&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.minimap.showSlider&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;always&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Adds a scrollbar to the minimap&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal.integrated.fontFamily&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;'MesloLGS NF', 'MesloLGL Nerd Font', monospace&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Change to your terminal font&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal.integrated.shell.linux&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/usr/bin/zsh&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Change to your default shell and check your OS key (ex: for mac use "shell.mac"),&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;files.eol&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// Force LF  line endings, useful if you use Windows and Linux/Mac&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;files.watcherExclude&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// Boosts performance by not watching for changes in these directories&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/.git/objects/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/.git/subtree-cache/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;**/node_modules/*/**&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.codeActionsOnSave&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;source.fixAll.eslint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="c1"&gt;// Set to true if you're enforcing ESLint. Disable if you're working on old projects that changed their linting config over time.&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eslint.validate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascript&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;javascriptreact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;typescriptreact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// use ESLint on these files&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eslint.workingDirectories&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./node_modules/eslint&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[javascript]&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;editor.defaultFormatter&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;esbenp.prettier-vscode&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;// use the Prettier extension to format javascript code&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;
  
  
  Linting &amp;amp; Code Formatting
&lt;/h2&gt;

&lt;p&gt;I've included there two settings which are for two extensions, &lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier - Code formatter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The first is, in my opinion, the best linter for Javascript around. Basically it will warn or throw errors if your code breaks common best practices rules. If you have never used it I strongly suggest you start doing so, and every time ESLint complains about something you think it's stupid, go read about it instead of disabling the linter for that line. ESLint has seriously helped me grow as a developer and while it's a well known extension, it's one I can't help mentioning here.&lt;/p&gt;

&lt;p&gt;Prettier on the other hand is a code formatter, it 'prettifies' your code. So when pasting something my editor, the code gets instantly formatted by Prettier.&lt;/p&gt;

&lt;p&gt;Something that I've seen in some projects is combining both ESLint and Prettier, meaning that if code style is off the linter will warn you and Prettier will also format your code according to the ESLint rules you have in place. &lt;/p&gt;

&lt;h2&gt;
  
  
  Snippets
&lt;/h2&gt;

&lt;p&gt;I honestly think I'm using far too many snippets extensions, despite the fact that I may or may not be using some on these list, who knows. I'm not a 'snippets' person. But from my extensions list, the ones that I use on a daily basis and actually know that they exist are, &lt;a href="https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets" rel="noopener noreferrer"&gt;ES7 React/Redux/GraphQL/React-Native snippets&lt;/a&gt;, &lt;a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense" rel="noopener noreferrer"&gt;Path Intellisense&lt;/a&gt; &amp;amp; &lt;a href="https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets" rel="noopener noreferrer"&gt;JavaScript (ES6) code snippets&lt;/a&gt;. Path Intellisense is great at auto completing your relative path imports, the other two I use mostly for JavaScript/React shortcuts. The big boss of auto complete and intellisense is &lt;a href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode" rel="noopener noreferrer"&gt;Visual Studio IntelliCode&lt;/a&gt;, developed by Microsoft itself, it provides an AI-based relevant code completion to your code editor and is a &lt;strong&gt;must have&lt;/strong&gt; for all VSCode users.&lt;/p&gt;

&lt;p&gt;Other cool 'auto complete' extensions I have include &lt;a href="https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets" rel="noopener noreferrer"&gt;Turbo Console Log&lt;/a&gt; which I use with the keyboard shortcut to log my highlighted variables, &lt;a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-close-tag" rel="noopener noreferrer"&gt;Auto Close Tag&lt;/a&gt;, a well known extension that works flawlessly even with self closing elements and my personal favorite &lt;a href="https://marketplace.visualstudio.com/items?itemName=steoates.autoimport" rel="noopener noreferrer"&gt;Auto Import&lt;/a&gt;, which automatically finds and parses code actions for imports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Productivity &amp;amp; Git
&lt;/h2&gt;

&lt;p&gt;On the bottom bar there you can see how much I've coded for the day, I'm using two extensions for time tracking and I'm going to recommend the one I've been using for the longest time which is &lt;a href="https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime" rel="noopener noreferrer"&gt;Waka Time&lt;/a&gt;. On the sidebar you can also see &lt;a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager" rel="noopener noreferrer"&gt;Project Manager&lt;/a&gt;'s logo which is an extension for quickly switching between projects from your sidebar. &lt;/p&gt;

&lt;p&gt;Also on the sidebar, we have two git extensions, &lt;a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens" rel="noopener noreferrer"&gt;Git Lens&lt;/a&gt; is the first and allows you to fully integrate git into your code editor by adding &lt;strong&gt;code&lt;/strong&gt; &lt;strong&gt;blame&lt;/strong&gt;, &lt;strong&gt;revision navigation&lt;/strong&gt; and file,&lt;strong&gt;line and branch&lt;/strong&gt; history all from your sidebar. The other one is &lt;a href="https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph" rel="noopener noreferrer"&gt;Git Graph&lt;/a&gt; which adds a neat graphical interface to your Source Control tab and allows you to quickly check your repository status.&lt;/p&gt;

&lt;p&gt;The last two items on the sidebar are &lt;a href="https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree" rel="noopener noreferrer"&gt;Todo Tree&lt;/a&gt;, which shows all &lt;code&gt;// TODO&lt;/code&gt;  comments on the current repository and the bane of our existence, &lt;a href="https://marketplace.visualstudio.com/items?itemName=Atlassian.atlascode" rel="noopener noreferrer"&gt;Jira&lt;/a&gt; also has a VSCode extension, which I only recently discovered, and offers a quick glimpse of your assigned tasks out of the box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Honorable Mentions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Makogan.hermes-comments" rel="noopener noreferrer"&gt;Hermes Comments&lt;/a&gt; has about 1k installs and is a simple extension that adds comment commands to your VSCode Command Palette (Ctrl + Shift + P). Simply highlight text, hit the command palette and create framed or sub section comment out of the highlighted text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ahmadawais.emoji-log-vscode" rel="noopener noreferrer"&gt;Emoji Log&lt;/a&gt; is one of the many surfacing extensions that prefix your commits with Emoji. I prefer this one because it actually adds a button to the Source Control tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=hoovercj.vscode-power-mode" rel="noopener noreferrer"&gt;Power Mode&lt;/a&gt; is another extension I didn't know I &lt;strong&gt;needed&lt;/strong&gt; until I had it. This baby adds up your keystrokes into combos and even has the option to add sparks and custom GIFs to your code editor.&lt;/p&gt;

&lt;p&gt;People, I was &lt;strong&gt;blind&lt;/strong&gt; before I started using &lt;a href="https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow" rel="noopener noreferrer"&gt;ident-rainbow&lt;/a&gt;, &lt;a href="https://marketplace.visualstudio.com/items?itemName=spywhere.guides" rel="noopener noreferrer"&gt;Guides&lt;/a&gt; and &lt;a href="https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer" rel="noopener noreferrer"&gt;Bracket Pair Colorizer&lt;/a&gt;. These are one of the most known and oldest extensions out there, and they're always present when I'm using VSCode, check them out if you haven't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Full List
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Theming &amp;amp; Layout
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=icao.electron-vue" rel="noopener noreferrer"&gt;Electron vue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=antfu.icons-carbon" rel="noopener noreferrer"&gt;Carbon Product Icons&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=s-nlf-fh.glassit" rel="noopener noreferrer"&gt;GlassIt-VSC&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/moxer-theme/moxer-icons-code" rel="noopener noreferrer"&gt;Moxer Icons&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Productivity &amp;amp; Git
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=softwaredotcom.swdc-vscode" rel="noopener noreferrer"&gt;Code Time&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ahmadawais.emoji-log-vscode" rel="noopener noreferrer"&gt;Emoji Log&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens" rel="noopener noreferrer"&gt;Git Lens&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph" rel="noopener noreferrer"&gt;Git Graph&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Makogan.hermes-comments" rel="noopener noreferrer"&gt;Hermes Comments&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=CKGrafico.icomoon-viewer" rel="noopener noreferrer"&gt;Icomoon Viewer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Atlassian.atlascode" rel="noopener noreferrer"&gt;Jira&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager" rel="noopener noreferrer"&gt;Project Manager&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Gruntfuggly.todo-tree" rel="noopener noreferrer"&gt;Todo Tree&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=WakaTime.vscode-wakatime" rel="noopener noreferrer"&gt;Waka Time&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Utils
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=dkundel.vscode-new-file" rel="noopener noreferrer"&gt;Advanced New Files&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=formulahendry.auto-close-tag" rel="noopener noreferrer"&gt;Auto Close Tag&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=steoates.autoimport" rel="noopener noreferrer"&gt;Auto Import&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=nick-rudenko.back-n-forth" rel="noopener noreferrer"&gt;Back &amp;amp; Forth&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments" rel="noopener noreferrer"&gt;Better Comments&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=CoenraadS.bracket-pair-colorizer" rel="noopener noreferrer"&gt;Bracket Pair Colorizer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=naumovs.color-highlight" rel="noopener noreferrer"&gt;Color Highlight&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=anseki.vscode-color" rel="noopener noreferrer"&gt;Color Picker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=spywhere.guides" rel="noopener noreferrer"&gt;Guides&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=tht13.html-preview-vscode" rel="noopener noreferrer"&gt;HTML Preview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=idleberg.icon-fonts" rel="noopener noreferrer"&gt;Icon Fonts&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=wix.vscode-import-cost" rel="noopener noreferrer"&gt;Import Cost&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow" rel="noopener noreferrer"&gt;ident-rainbow&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=softwaredotcom.music-time" rel="noopener noreferrer"&gt;Music Time for Spotify&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=hoovercj.vscode-power-mode" rel="noopener noreferrer"&gt;Power Mode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-wsl" rel="noopener noreferrer"&gt;Remote - WSL&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync" rel="noopener noreferrer"&gt;Settings Sync&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=richie5um2.vscode-sort-json" rel="noopener noreferrer"&gt;Sort JSON objects&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=meganrogge.template-string-converter" rel="noopener noreferrer"&gt;Template String Converter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets" rel="noopener noreferrer"&gt;Turbo Console Log&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Languages &amp;amp; Snippets
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=mgmcdermott.vscode-language-babel" rel="noopener noreferrer"&gt;Babel JavaScript&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=mikestead.dotenv" rel="noopener noreferrer"&gt;DotENV&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets" rel="noopener noreferrer"&gt;ES7 React/Redux/GraphQL/React-Native snippets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=Zignd.html-css-class-completion" rel="noopener noreferrer"&gt;IntelliSense for CSS class names in HTML&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets" rel="noopener noreferrer"&gt;JavaScript (ES6) code snippets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-next" rel="noopener noreferrer"&gt;JavaScript and TypeScript Nightly&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=cmstead.jsrefactor" rel="noopener noreferrer"&gt;JSRefactor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-toolsai.jupyter" rel="noopener noreferrer"&gt;Jupyter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one" rel="noopener noreferrer"&gt;Markdown All in One&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.npm-intellisense" rel="noopener noreferrer"&gt;npm Intellisense&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense" rel="noopener noreferrer"&gt;Path Intellisense&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=ms-python.python" rel="noopener noreferrer"&gt;Python&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=OfHumanBondage.react-proptypes-intellisense" rel="noopener noreferrer"&gt;React PropTypes Intellisense&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=castwide.solargraph" rel="noopener noreferrer"&gt;Ruby Solargraph&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=jonkwheeler.styled-components-snippets" rel="noopener noreferrer"&gt;styled-components-snippets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=infeng.vscode-react-typescript" rel="noopener noreferrer"&gt;Typescript React code snippets&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.vscodeintellicode" rel="noopener noreferrer"&gt;Visual Stadio IntelliCode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=planbcoding.vscode-react-refactor" rel="noopener noreferrer"&gt;VSCode React Refactor&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=wingrunr21.vscode-ruby" rel="noopener noreferrer"&gt;VSCode Ruby&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml" rel="noopener noreferrer"&gt;YAML&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Linting
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" rel="noopener noreferrer"&gt;ESLint&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint" rel="noopener noreferrer"&gt;markdownlint&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" rel="noopener noreferrer"&gt;Prettier - Code formatter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://marketplace.visualstudio.com/items?itemName=adamwalzer.scss-lint" rel="noopener noreferrer"&gt;scss-lint&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow Me
&lt;/h2&gt;

&lt;p&gt;Thanks if you ended up reading through the whole list 💪 For more JS/React content you can check out my &lt;a href="http://dsmorais.com" rel="noopener noreferrer"&gt;website&lt;/a&gt; and follow me on &lt;a href="https://twitter.com/davidsmorais" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Let me know what extensions you're using that I did not mention in here so I can check them out 👀&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>extensions</category>
    </item>
    <item>
      <title>You (may not) Know JS</title>
      <dc:creator>David  Morais</dc:creator>
      <pubDate>Wed, 14 Apr 2021 09:05:04 +0000</pubDate>
      <link>https://dev.to/davidmorais/you-may-not-know-js-9i</link>
      <guid>https://dev.to/davidmorais/you-may-not-know-js-9i</guid>
      <description>&lt;p&gt;Hello my fellow JavaScript developers 👋&lt;/p&gt;

&lt;p&gt;Back in my first job I quickly realized that the &lt;a href="http://www.freecodecamp.com" rel="noopener noreferrer"&gt;FreeCodeCamp&lt;/a&gt; front end course I had finished wasn't nearly enough to deal with the hardships of creating scalable and maintainable D3 chart templates. A fact that was confirmed when my boss &lt;em&gt;suggested&lt;/em&gt; I read more about the insides of the language, heavily implying that I'd be fired if I didn't 🚀&lt;/p&gt;

&lt;p&gt;My senior developer at the time suggested the well known &lt;a href="https://github.com/getify/You-Dont-Know-JS" rel="noopener noreferrer"&gt;You Don't Know JS&lt;/a&gt; books, a well written series of books about the intricacies of the language. And by the end of the first book I had realized that I did not have the foundations of the language, and acquiring them made me more productive by cutting down on the time spent googling around how stuff was supposed to work.&lt;/p&gt;

&lt;p&gt;So the goal of this post is not so much to imply that you don't know how to declare variables as to declare that you, my friend, may not always be aware of what's going on under the hood and teach you some use cases for those mechanisms.&lt;/p&gt;

&lt;p&gt;And without further delay, let's list some quick facts and concepts you probably did not know about JS&lt;/p&gt;

&lt;h2&gt;
  
  
  Type coercion
&lt;/h2&gt;

&lt;p&gt;Type coercion is the process of converting value from one type to another. Since JavaScript is a weakly-typed language, it converts two different typed variables when you use its operators. &lt;/p&gt;

&lt;p&gt;A great cheat sheet for the type coercion principles can be found &lt;a href="https://dorey.github.io/JavaScript-Equality-Table/" rel="noopener noreferrer"&gt;here&lt;/a&gt; 👈 If you're still wondering, the best practice is to not learn that whole table and stick with using &lt;strong&gt;strict comparison. REALLY.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's get to some quick facts straight about operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Difference between &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;===&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;There a difference between using &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;===&lt;/code&gt; when comparing two variables. The first only compares value, it's called &lt;strong&gt;abstract equality&lt;/strong&gt;, while the latter compares type and value and is called &lt;strong&gt;strict comparison&lt;/strong&gt;. This is why &lt;code&gt;1 == "1" //true&lt;/code&gt; and &lt;code&gt;1 === "1" //false&lt;/code&gt; .  In the first comparison we have  &lt;em&gt;implicit coercion&lt;/em&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Difference between &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When &lt;strong&gt;strictly&lt;/strong&gt; comparing &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt;, JavaScript returns &lt;code&gt;false&lt;/code&gt; , this is because &lt;code&gt;undefined&lt;/code&gt; is the default value for undeclared values, function that doesn't return anything or an object property which doesn't exist. While &lt;code&gt;null&lt;/code&gt; is a value that has to be explicitly given to a variable or returned by a function.&lt;/p&gt;

&lt;p&gt;In the end if you also check, the type of both variables is different. &lt;code&gt;typeof null //"object"&lt;/code&gt; and &lt;code&gt;typeof undefined //"undefined"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Short Circuiting Logical Operators
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Because who needs ternaries&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now here's another example of where JavaScript's type coercion comes into play. For rendering React components, you come across the following patter quite often: &lt;code&gt;const render = () =&amp;gt; loading &amp;amp;&amp;amp; &amp;lt;Component/&amp;gt;&lt;/code&gt; . Usually &lt;code&gt;loading&lt;/code&gt; is already a Boolean type variable, but sometimes we can find something like &lt;code&gt;const render = () =&amp;gt; data.length  &amp;amp;&amp;amp; &amp;lt;Component data={data}/&amp;gt;&lt;/code&gt; and in this case &lt;code&gt;data.length&lt;/code&gt; can is &lt;em&gt;truthy&lt;/em&gt; when its value is &lt;strong&gt;not&lt;/strong&gt; 0. &lt;/p&gt;

&lt;p&gt;Combining &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; and &lt;code&gt;||&lt;/code&gt; operators is also a great way to add logic to arrow functions without requiring you to create a block: &lt;code&gt;const render = () =&amp;gt; data.length &amp;amp;&amp;amp; loading &amp;amp;&amp;amp; &amp;lt;Component/&amp;gt; || 'Loading'&lt;/code&gt;. In this example, you basically create a ternary condition in which you evaluate the first conditions of the &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; half  and return the last condition if the others evaluate to &lt;code&gt;true&lt;/code&gt;, in this case our component, OR we return a loading String or in case we don't want to show anything, &lt;code&gt;null&lt;/code&gt; or an empty string. &lt;/p&gt;

&lt;h3&gt;
  
  
  Nullish Operators
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Recently&lt;/em&gt; JavaScript got a couple of new features that tap into its weakly-typed nature and make use of under the hood type coercion to work.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;nullish coalescing operator (??)&lt;/strong&gt; is a logical operator that returns its right-hand side operand when its left-hand side operand is &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;, and otherwise returns its left-hand side operand.&lt;/p&gt;

&lt;p&gt;This means that we can also use it to add logic to our variable declarations, but works differently than the AND &amp;amp; OR operators. Here's an example:&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%2F0dlmosk82nb4fbunwrzu.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%2F0dlmosk82nb4fbunwrzu.png" alt="Nullish.png" width="800" height="528"&gt;&lt;/a&gt;&lt;br&gt;
Using the nullish coalescing operator to declare &lt;code&gt;obj&lt;/code&gt;'s properties, will result in the following object &lt;/p&gt;

&lt;p&gt;While we're here, we could've also used the &lt;strong&gt;optional chaining operator (?.)&lt;/strong&gt; to access &lt;code&gt;obj.woo.length&lt;/code&gt;. You may very well be familiar with the "Cannot read 'length' of undefined" error, and if you remember to use this, those days are gone.  What is it and how do you use it ? Just add a &lt;code&gt;?&lt;/code&gt; when accessing object properties that may be &lt;code&gt;null&lt;/code&gt; or &lt;code&gt;undefined&lt;/code&gt;. In the above example, we would've written something like &lt;code&gt;obj.tar = obj?.woo?.length ?? ["WOOTAR"]&lt;/code&gt; . If &lt;code&gt;obj.woo&lt;/code&gt; was &lt;em&gt;nullish&lt;/em&gt; the output would also be different, as the condition would evaluate to &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;obj.tar=["WOOTAR"]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At last, there's the &lt;strong&gt;logical nullish assignment (??=)&lt;/strong&gt; which only assigns a value if the lefy-hand operator is &lt;em&gt;nullish.&lt;/em&gt; As an example, let's add more properties to our &lt;code&gt;obj&lt;/code&gt; using the logical nullish assignment: &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%2F7t5h9x11ty7pvx3p0yk8.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%2F7t5h9x11ty7pvx3p0yk8.png" alt="logical nullish.png" width="800" height="528"&gt;&lt;/a&gt;&lt;br&gt;
Using the logical nullish assignment to assign the &lt;code&gt;[obj.bar](http://obj.bar)&lt;/code&gt; property results in the following output&lt;/p&gt;

&lt;p&gt;These are all JavaScript features which use the underlying type coercion mechanism. And while Logical Operators may be something you use on a daily basis, understanding how the language treats different type operations can really help developers do their jobs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hoisting
&lt;/h2&gt;

&lt;p&gt;Hoisting is another one of JS's under the hood mechanics which affects your daily work. If you use ES Lint, and as a junior you seriously should &lt;em&gt;consider using it&lt;/em&gt;, you've probably come across the &lt;code&gt;no-use-before-define&lt;/code&gt; &lt;a href="https://eslint.org/docs/rules/no-use-before-define" rel="noopener noreferrer"&gt;rule&lt;/a&gt;. This discourages you from using variables before declaring them, and before ES6 introduced &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt;, this rule was in place for readability purposes. This is because you can, in fact, use variables before declaring them, as long as they're declared within scope. I'll explain.&lt;/p&gt;

&lt;p&gt;In most languages we have two contexts in which the code is read, in JS we have what's usually called &lt;strong&gt;Compile Time&lt;/strong&gt; and &lt;strong&gt;Execution Time.&lt;/strong&gt; Code is compiled before it is executed, and during JavaScript's compile time, it &lt;strong&gt;hoists&lt;/strong&gt; all functions and variables and while functions retain their declaration value, for variables the hoisting process gives them a value of &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Example:  &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%2Frg6r4dywg5wfqvk4fph6.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%2Frg6r4dywg5wfqvk4fph6.png" alt="hoisting.png" width="800" height="528"&gt;&lt;/a&gt;&lt;br&gt;
Here's what our code looks like on Compile vs Execution Time&lt;/p&gt;

&lt;p&gt;This code will log &lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;David&lt;/code&gt; and &lt;code&gt;"Hello Mark!"&lt;/code&gt; . This is because when hoisted to the top of the scope, our variable will get the value of &lt;code&gt;undefined&lt;/code&gt; until it's explicitly set.&lt;/p&gt;

&lt;p&gt;With ES6' introduction of the &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; keywords, hoisting is becoming obsolete, in the sense of its use cases disappearing, because only the &lt;code&gt;var&lt;/code&gt; and &lt;code&gt;function&lt;/code&gt; keywords are hoisted. The same applies to arrow functions. &lt;/p&gt;

&lt;p&gt;Notice how I intentionally used the same &lt;code&gt;name&lt;/code&gt; for both our global variable and the &lt;code&gt;sayHello&lt;/code&gt; function parameter ? Yes, we'll be talking about...&lt;/p&gt;

&lt;h2&gt;
  
  
  Scopes
&lt;/h2&gt;

&lt;p&gt;Scope is simply the 'biome' in which our declared variables live in. In JavaScript we have the &lt;strong&gt;global scope&lt;/strong&gt; and &lt;strong&gt;function scope.&lt;/strong&gt; In the above example, &lt;code&gt;name&lt;/code&gt; lives in the global scope, but when a function has a parameter with the same name, it takes precedence. This is because JavaScript will look for a variable declaration in the current scope and if it doesn't find it, it will move up to the next scope, in our case, the global scope. ES6 also introduced the &lt;strong&gt;block scope,&lt;/strong&gt; by using &lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; keywords, you are declaring variables that are only available within a block (&lt;code&gt;{}&lt;/code&gt;) . Let's see an example 👇&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%2F2jaff6ufpzg6lzpqczia.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%2F2jaff6ufpzg6lzpqczia.png" alt="Scope.png" width="800" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we use &lt;code&gt;let&lt;/code&gt; to declare our variable, it will be only accessible within its block scope, in this case, within the &lt;code&gt;if&lt;/code&gt; condition, and will receive an error if we try to use it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Closures
&lt;/h3&gt;

&lt;p&gt;Here's something that usually comes up in interviews. &lt;em&gt;What are closures ?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my opinion, this is a rather stupid question to ask, as it's one of those JS under the hood mechanisms that developers make use of all the time, but don't even realize it exists, much less its name. I'll quote &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures" rel="noopener noreferrer"&gt;MDN&lt;/a&gt; here: "&lt;em&gt;a closure gives you access to an outer function’s scope from an inner function.&lt;/em&gt;".&lt;/p&gt;

&lt;p&gt;Let's go back to our poorly worded &lt;code&gt;sayHello&lt;/code&gt; example, let's strip it of the console.logs, the hoisting logic, and &lt;strong&gt;remove the function parameter&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fct4d55z4ka4pnsojxz1y.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%2Fct4d55z4ka4pnsojxz1y.png" alt="Closure.png" width="800" height="508"&gt;&lt;/a&gt;&lt;br&gt;
A wild closure appears&lt;/p&gt;

&lt;p&gt;BAM, lo and behold, a &lt;strong&gt;closure&lt;/strong&gt;. Not that complicated, and something we use on a &lt;del&gt;daily&lt;/del&gt; hourly basis, but admittedly one of the worst concepts to try and describe in words. &lt;/p&gt;

&lt;p&gt;Now, one important aspect of closures is that the variables used within them are &lt;strong&gt;not copies,&lt;/strong&gt; this means that if you change a variable within a function, its value is changed for all the scope its being used on. So if within &lt;code&gt;sayHello&lt;/code&gt; I were to set &lt;code&gt;name = 'Matt'&lt;/code&gt; , the variable would change for the rest of the execution, depending on where I'd call &lt;code&gt;sayHello&lt;/code&gt; .&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;There are many more 'hidden' aspects of JavaScript which I'd like to discuss, and probably will in the future, like the Protype, Inheritance and (IIFE)().  What are your tricks and how do you use these JS's hidden gems ? Let me know in the comments.&lt;/p&gt;

&lt;p&gt;If you liked this post, follow me on &lt;a href="https://twitter.com/davidsmorais" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, mostly for stupid and pointless stuff, and be sure to checkout &lt;a href="http://dsmorais.com" rel="noopener noreferrer"&gt;my website&lt;/a&gt; as I'll try to create new content at least twice per month.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>closures</category>
      <category>hoisting</category>
      <category>languages</category>
    </item>
  </channel>
</rss>
