<?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: Eryk Napierała</title>
    <description>The latest articles on DEV Community by Eryk Napierała (@erykpiast).</description>
    <link>https://dev.to/erykpiast</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%2F57560%2F6ccc0b8d-f8b8-4939-a153-bdb0d2fc6879.jpeg</url>
      <title>DEV Community: Eryk Napierała</title>
      <link>https://dev.to/erykpiast</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/erykpiast"/>
    <language>en</language>
    <item>
      <title>wishihadbetter.tools</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Fri, 28 Feb 2025 10:45:00 +0000</pubDate>
      <link>https://dev.to/erykpiast/wishihadbettertools-iog</link>
      <guid>https://dev.to/erykpiast/wishihadbettertools-iog</guid>
      <description>&lt;p&gt;Getting people’s attention these days isn’t easy. Your social media post or blog entry competes with hundreds of others published every day, and people get distracted easily. You might spend hours perfecting a 500-word piece, only for five people to read it. &lt;a href="https://dev.to/erykpiast/theres-only-01-chance-youll-read-this-post-1pe3"&gt;That’s how it goes when you’re not a celebrity&lt;/a&gt;. It doesn’t have to be that way. An extraordinary website that conveys the same idea that a blog post would can make a difference.&lt;/p&gt;

&lt;p&gt;This year, &lt;a href="https://dev.to/erykpiast/wish-i-had-better-tools-329c"&gt;I want to spend 500 hours solving one small problem for people working on digital products&lt;/a&gt;—people like me. The first challenge is choosing a topic. It can’t be a huge, complex issue needing a big team and years of engineering. It should be solvable by one person in their spare time. I’m not focused on financial value. Sometimes, everyday annoyances go unaddressed because they aren’t profitable. That’s the niche I’m aiming for.&lt;/p&gt;

&lt;p&gt;To figure out people’s biggest struggles, I need enough data from the right audience—about 1,000 entries, so I can spot patterns and decide where to dive deeper. I won’t get that by just posting a question on social media; I’ve barely used it over the last decade, so my reach is tiny. Talking to people I know directly might yield 50 answers at most. Still, I believe I can spark enough interest!&lt;/p&gt;

&lt;p&gt;Because I’m a builder at heart, I think people value something that stands out. That’s how I came up with &lt;a href="https://wishihadbetter.tools/" rel="noopener noreferrer"&gt;wishihadbetter.tools&lt;/a&gt;—a very simple website with just one input field for typing your wish for better tooling. No authentication, no cookies—just a basic web form. After submitting, you can see what other people have wished for.&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%2Fl8o7mkb24iw9k39kh9kl.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%2Fl8o7mkb24iw9k39kh9kl.png" alt="The initial view with the form on the wishihadbetter.tools website" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdtcg6vojxenv905xl5il.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%2Fdtcg6vojxenv905xl5il.png" alt="The wishlist view on the wishihadbetter.tools website" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this public database of software requests helps not only me but also other creators who want to build something meaningful and make the world a little bit better.&lt;/p&gt;

&lt;p&gt;Missing a piece of software? Maybe you need an entirely new tool, or just a better version of something you already use. &lt;a href="https://wishihadbetter.tools/" rel="noopener noreferrer"&gt;Share your wish at wishihadbetter.tools&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>sideprojects</category>
      <category>buildinpublic</category>
      <category>webdev</category>
    </item>
    <item>
      <title>There's only 0.2% chance you'll read this post</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Wed, 19 Feb 2025 11:15:00 +0000</pubDate>
      <link>https://dev.to/erykpiast/theres-only-01-chance-youll-read-this-post-1pe3</link>
      <guid>https://dev.to/erykpiast/theres-only-01-chance-youll-read-this-post-1pe3</guid>
      <description>&lt;p&gt;I recently &lt;a href="https://dev.to/erykpiast/wish-i-had-better-tools-329c"&gt;had this brilliant idea&lt;/a&gt; to spend two hours every day for the rest of the year building a tool that solves one small problem for people working on digital products. My first step was posting the idea on social media and asking what people struggle with. The response I got was surprising!&lt;/p&gt;

&lt;p&gt;To give some context—I’m not a social media beast. I’m more of a consumer than a creator. This time, to make the project successful (and to challenge my comfort zone), I shared the post in three places:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;dev.to&lt;/strong&gt; – I’ve had an account there for a while, though I wasn’t a regular publisher. Over time, I got around 3,000 followers, but each post usually got a few hundred views. This time, I got &lt;strong&gt;33 views&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Substack&lt;/strong&gt; – This account is brand new, and the post was my first solo publication. It got &lt;strong&gt;80 views&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LinkedIn&lt;/strong&gt; – I promoted the Substack post on LinkedIn. I’ve been on LinkedIn for 11 years but hadn’t posted anything until recently. This was my second post, and in a week, the message reached &lt;strong&gt;450 people&lt;/strong&gt;. Of those, 60 actually opened the Substack article.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So, my idea reached around 500 people in total. Of those, 110 opened the article, and I can confirm that at least one person read it thoroughly—I got one comment replying to my question about missing tools. That’s a total conversion rate of about &lt;strong&gt;0.2%&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Another way I tried to spark engagement was direct conversation. I reached out to a group of around 160 former coworkers, asking them about the tools they miss. Three people responded, which gave me a 2% conversion rate—10 times higher than “on the Internet”! Then, in chats with three other friends, I got four new ideas—half of my total responses so far.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons and next steps
&lt;/h3&gt;

&lt;p&gt;These results are not great, but I didn’t have any expectations. I’m aware that marketing is tough, and I’m out of practice. I pulled back from social media a long time ago, hardly commenting or posting. I imagine the content algorithms barely recognize me. In the end, though, it was an experiment: I gathered data and learned something. That’s what experiments are for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bully more people personally&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
I’m convinced that if I ask folks directly about the struggles they face, I’ll get plenty of high-quality answers. I believe I maintain a good enough relationship with at least 50 ex-colleagues who’d help me if I reached out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Try to reach a much broader audience&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
With a 0.2% conversion rate and minimal social media influence, I’d need to reach half a million people to get 1,000 answers. That’s arbitrary, but it’s a big enough sample to see patterns and decide where to focus. So, how do I get that kind of reach?&lt;/p&gt;

&lt;p&gt;My idea is to appear in one of those popular newsletters about online product development: &lt;a href="https://tldr.tech/" rel="noopener noreferrer"&gt;TLDR&lt;/a&gt;, &lt;a href="https://sidebar.io/" rel="noopener noreferrer"&gt;sidebar.io&lt;/a&gt;, or &lt;a href="https://bytes.dev/" rel="noopener noreferrer"&gt;Bytes&lt;/a&gt;. Each one has hundreds of thousands of subscribers. If I manage to get featured, I might collect hundreds of answers from people I don’t know and couldn’t reach otherwise. Of course, it won’t be easy. Just publishing a post with a survey link isn’t enough—I need to create something worthwhile.&lt;/p&gt;

&lt;p&gt;That’s why I reserved a new domain and I'll fill it with something unique: &lt;a href="https://wishihadbetter.tools" rel="noopener noreferrer"&gt;wishihadbetter.tools&lt;/a&gt;. Stay tuned!&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>sideprojects</category>
      <category>buildinpublic</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Wish I Had Better Tools</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Mon, 10 Feb 2025 11:00:00 +0000</pubDate>
      <link>https://dev.to/erykpiast/wish-i-had-better-tools-329c</link>
      <guid>https://dev.to/erykpiast/wish-i-had-better-tools-329c</guid>
      <description>&lt;p&gt;This year, it’s been about 20 years since I started coding. I was a teenager when I got my first Windows PC and a yellow book about web development. For the first few years, I had no idea what I was doing, but it was probably the best time of my career. I had ten thousand ideas every week. Everything was exciting and new, and everything seemed possible. I remember the dopamine rush whenever I solved a problem! Today, I have 13 years of professional experience building SaaS products, and I feel… very hungry for that feeling again.&lt;/p&gt;

&lt;p&gt;Throughout my career I was building early-stage startups that failed after a few months, and I also was part of a successful scale-up that was eventually sold out for a 9-figure sum. I worked with small companies of just a few people, as well as corporations employing thousands. For what it’s worth, I eventually reached the Senior Staff Software Engineer title in a big organisation! I’m sure I still have a lot to learn, but I can venture to say that I know something about software development at this point. I know how to plan and deliver a project, how to assess risks, how to make trade-offs, and which battles to pursue (most of the time). I feel I can use these skills for something more than day-to-day work in another VC-funded startup, which, realistically, can succeed with or without me.&lt;/p&gt;

&lt;p&gt;Now, you might be wondering what the title of this post means and how it relates to all this. First of all, I truly cannot complain about the tools I had when I was learning web development. I had everything I needed—IDEs, debuggers, local servers, developer tools, and learning materials… and most of them were free and open source! I’m grateful to all the engineers before me who built these tools. You made my career possible—thank you. Yet, I believe there are still many problems to solve in engineering, and we’re far from a perfect state. It’s not about AI or groundbreaking innovations, though.&lt;/p&gt;

&lt;p&gt;Everyone who builds software (or really anything else) has these moments: “Sigh! I wish I had a better tool for this!” Often, “this” is something small and not very important. It might be a tedious task you don’t do very often, but it annoys you every single time. Or maybe it’s a regular workflow that’s just unpleasant, yet not enough to bother fixing. It’s like having a tiny pebble in your shoe during a winter mountain trip—taking your shoe off in the cold is the last thing you want to do. Still, it would be so nice to get that pebble out and make the rest of the trip more comfortable!&lt;/p&gt;

&lt;p&gt;So, I’m looking for your pebbles—I want to learn what these little things you struggle with in your day-to-day work are. The plan is simple: In 2025, I want to invest 10 hours of my time every week in helping others build software. By the end of the year, that will add up to 500 hours—about three months of a full-time job. That seems like enough time to solve one small problem that a decent number of people experience.&lt;/p&gt;

&lt;p&gt;I plan to share the progress of this project on this blog. Yes, writing will eat into the time budget (this post alone took me two hours; I’m a slow writer!), but I believe it’s worth it. The journey is just as important as the destination.&lt;/p&gt;

&lt;p&gt;Now, please help me kick this off! Finish the sentence below in the comments:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I wish I had a better tool for…&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(If you prefer anonymous response, &lt;a href="https://surveys.hotjar.com/bba17224-859c-4436-ba32-23511c596cbb" rel="noopener noreferrer"&gt;use the survey link&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>tooling</category>
      <category>sideprojects</category>
      <category>buildinpublic</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The guiding light of a North Star - Bringing long-term vision to our Frontend transformation at Hotjar</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Fri, 20 Dec 2024 13:22:15 +0000</pubDate>
      <link>https://dev.to/erykpiast/the-guiding-light-of-a-north-star-bringing-long-term-vision-to-our-frontend-transformation-at-37ci</link>
      <guid>https://dev.to/erykpiast/the-guiding-light-of-a-north-star-bringing-long-term-vision-to-our-frontend-transformation-at-37ci</guid>
      <description>&lt;p&gt;Striking a balance between delivering new features and investing in technical improvements is a challenge every SaaS company faces. In product-driven organizations, engineering leaders must champion initiatives that may not provide immediate customer value but are essential for long-term sustainability. At Hotjar, we embarked on a complex journey to modernize the architecture of our main front-end application. It was a three-year endeavor filled with twists and turns, but we emerged victorious—and with a wealth of knowledge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our North Star
&lt;/h2&gt;

&lt;p&gt;Rewind to the latter half of 2020. The COVID-19 pandemic was in full swing, but as a remote-first company, Hotjar was uniquely positioned to weather the storm. Our focus remained fixed on the future and the challenges ahead.&lt;/p&gt;

&lt;p&gt;Our main front-end project, the Insights Webapp, was a monolithic application pushing 200k+ lines of code. With a growing team of 20 engineers and an active hiring pipeline, the cracks were starting to show. Incidental coupling led to conflicts, Continuous Integration times ballooned, and Continuous Delivery became a bottleneck due to a shared deployment pipeline with the backend system. We needed a scalable, future-proof solution.&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%2Fhaq45d0op719pqz5t1se.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%2Fhaq45d0op719pqz5t1se.png" alt="North Star image generated with Gemini" width="800" height="665"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inspired by our backend colleagues, we’ve set out to define our "North Star"—a set of guiding principles for our front-end platform. A small group of senior engineers and engineering leaders convened weekly to dissect the challenges and brainstorm solutions. After a few months of deliberation, our Frontend North Star emerged:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ownership&lt;/li&gt;
&lt;li&gt;Autonomy and Accountability&lt;/li&gt;
&lt;li&gt;Easy Deployment&lt;/li&gt;
&lt;li&gt;Performance and Metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While the "how" remained unclear, the "what" was crystal clear. These four pillars would underpin our efforts for the next three years.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #1&lt;/strong&gt;: &lt;em&gt;In complex projects, meticulous upfront planning isn't always necessary. Aligning on core principles is paramount—the details can be ironed out along the way.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Transparency is a core value at Hotjar. We practiced it by sharing our thoughts and progress on an internal blogging platform. Our posts sparked discussions with backend colleagues, leading to the creation of a dedicated Slack channel for exchanging ideas and experiences. This open forum proved invaluable in refining our problem definition and exploring solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #2&lt;/strong&gt;: &lt;em&gt;Transparency works miracles. The sooner you share your idea, the more help you can get from people around you.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Embracing the Modular Monolith
&lt;/h2&gt;

&lt;p&gt;With our guiding principles established, we turned our attention to identifying technical solutions that aligned with our vision. Simplicity and proven architectures were important – we wanted to avoid the pitfalls of unproven experimental technology. In early 2021, the modular monolith, built upon a monorepo foundation, emerged as a leading contender. We believed this approach, with its clearly defined NPM package boundaries, would deliver on our pillars of &lt;strong&gt;Ownership&lt;/strong&gt; and &lt;strong&gt;Autonomy&lt;/strong&gt;. The goal wasn't to completely refactor the codebase but to reorganize and simplify dependencies. It’s what the diagrams below present - they have a similar number of communication flows and the right one has even more components, but it's also evident that the new architecture’s flows (on the right) are much easier to understand.&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%2Fjhqvar3tdt4o8bmffwss.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%2Fjhqvar3tdt4o8bmffwss.png" alt="Communication flow difference between a monolith and a modular monolith&amp;lt;br&amp;gt;
" width="800" height="335"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, our decision to decouple the front-end and back-end code into separate repositories and deployment pipelines would support our &lt;strong&gt;Easy Deployment&lt;/strong&gt; objective. After evaluating various monorepo tools, we settled on NPM v7 workspaces and Lerna, a lightweight solution that seamlessly integrated with our existing infrastructure.&lt;/p&gt;

&lt;p&gt;Upgrading our package manager was a necessary first step, a task that demanded considerable time and effort. Fortunately, a compelling motivator emerged: the opportunity to contribute to a high-stakes rebranding project. While we were defining our North Star, the product team had initiated a rebranding and application redesign, including the development of Hotjar's first full-fledged design system. This design system was architected as a separate repository with an NPM package published to our internal registry. While it facilitated rapid iterations, integrating the library into the web app proved cumbersome. Managing updates and bug fixes across two repositories was inefficient. The design system team needed a more streamlined workflow, and the monorepo approach promised to deliver. This situation allowed us to justify the investment in the monorepo infrastructure, highlighting its immediate benefits for a priority business objective, in addition to its long-term value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #3:&lt;/strong&gt; &lt;em&gt;Capitalize on opportunities to align your technical initiatives with business goals, even if they seem like "local optimizations." Every bit of support helps propel your plan forward.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Crowdsourcing
&lt;/h2&gt;

&lt;p&gt;Alongside the infrastructure, we furnished teams with a basic template for new packages and architecture guidelines. The integration of the design system served as our first practical example. We contemplated the ideal structure for new packages and the patterns we should promote and discourage. Admittedly, we lacked concrete answers to many of our questions, given our limited experience with the new packaged architecture. We embraced a "learning by doing" approach, providing minimal guidance to the squads. We believed our talented engineers, immersed in product development, were best positioned to identify pain points and propose solutions.&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%2Frx57m0t3qq5q7fbpxuqe.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%2Frx57m0t3qq5q7fbpxuqe.png" alt="An illustration of crowdsourcing generated by ChatGPT" width="800" height="876"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This strategy sparked the emergence of new modules. Engineers from various squads enthusiastically experimented with the new tooling, creating new packages and extracting code from the existing codebase. Their hands-on experience yielded invaluable feedback and illuminated real-life challenges. This iterative process allowed us to refine our tooling and documentation, addressing the most pressing issues and empowering teams to build better products. This was a testament to Hotjar's Core Values in action: engineers challenged themselves, worked respectfully, and moved fast, releasing work in small increments.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #4:&lt;/strong&gt; &lt;em&gt;Avoid overthinking and strive for minimal viable solutions. Observe, listen, and iterate based on real-world feedback.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Measuring Progress
&lt;/h2&gt;

&lt;p&gt;Our approach wasn't purely reactive. We continuously evaluated the evolving technology landscape and its alignment with our North Star. As 2022 dawned, we bid farewell to Lerna and fully embraced NPM Workspaces, which had matured enough to meet our needs. We also introduced NX, a powerful task manager designed for monorepo architectures. Theoretically, NX promised faster Continuous Integration through intelligent task isolation and dependency resolution. However, NX also brought to light a lurking issue: circular dependencies.&lt;/p&gt;

&lt;p&gt;We had anticipated this challenge during the transition period, as we extracted code from the legacy system into packages while retaining the old codebase as the application's entry point. Maintaining package purity during migration wasn't pragmatic, as new code often needed to reference legacy modules. This created the inevitable circular dependency. While starting the migration from core components ("leaves" of the dependency graph) would have been ideal, these lacked clear ownership, making it challenging to engage product engineers. We opted to accept the suboptimal architecture temporarily.&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%2F1kqecdatfi29dlj6cg04.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%2F1kqecdatfi29dlj6cg04.png" alt="Bar chart with circular dependencies per area of ownership&amp;lt;br&amp;gt;
" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This marked our first foray into the fourth pillar of our North Star: &lt;strong&gt;Performance and Metrics&lt;/strong&gt;. We introduced a metric to track the number of circular dependencies between packages, making it visible to the entire team. Initially, NX reported a staggering 1800 circular dependency chains! This underscored the importance of early monitoring. The presence of these dependencies negated the benefits of NX's sophisticated task isolation capabilities. While not entirely unexpected, this realization highlighted a communication gap. We had emphasized the improved developer experience that modularization would bring, but that reality was still distant. As we were progressing with the modularisation, oftentimes we had to make tactical compromises, and our metric wasn’t always continuously decreasing. That wasn’t the point - what mattered was that we knew how far we’re from the goal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #5:&lt;/strong&gt; &lt;em&gt;Define your metrics early and begin data collection immediately. Early insights illuminate potential problems and enable better decision-making.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Exploring Microfrontends
&lt;/h2&gt;

&lt;p&gt;Concurrently, we initiated another stream of work focused on the Easy Deployment pillar. Our reasoning was that while our packages were currently tightly coupled, they wouldn't remain so indefinitely. This led us to explore the possibility of deploying them independently. NX offered built-in support for Webpack Module Federation, presenting an ideal opportunity to delve into this technology, even though we hadn't yet encountered the specific problem it addressed. With a peak development team of 30 engineers, and having decoupled our deployment pipeline from the backend, there wasn't a pressing need for isolated deployments. However, the concept of microfrontends had intrigued us from the outset of our North Star discussions.&lt;/p&gt;

&lt;p&gt;While we had intentionally defined our North Star in terms of desired outcomes rather than specific solutions, we couldn't help but contemplate implementation details. This wasn't necessarily a mistake—the work was engaging and yielded valuable learning experiences. However, justifying the investment in this stream proved challenging. For several quarters, it remained a speculative endeavor. Convincing leadership to allocate resources based solely on the promise of future need was difficult. Engineers working on this stream grappled with motivation and questioned the significance of their efforts.&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%2Fpojviq92ugjo2cuiy33k.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%2Fpojviq92ugjo2cuiy33k.png" alt="Microfrontend high-level architecture&amp;lt;br&amp;gt;
" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fortunately, the product team presented another timely opportunity. Hotjar acquired PingPong, a small startup, and decided to integrate its offering into our application. After considering various microfrontend architectures, the newly formed integration team eagerly embraced one experimental microfrontend infrastructure to maintain autonomy while delivering a seamless customer experience. This provided the real-world testing ground we needed, allowing us to connect our prototypes to tangible product needs. The work ultimately proved valuable and successful, but the path to that success could have been smoother.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #6:&lt;/strong&gt; &lt;em&gt;In a product-led organization, evaluate ideas through the lens of customer needs. Many excellent solutions may not be relevant to your current context. Recognize that timing is crucial to avoid frustration and maximize impact.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Evolving Iterations
&lt;/h2&gt;

&lt;p&gt;Throughout the remainder of the year, we steadily progressed with our modularization efforts across various domain areas. We launched a more structured initiative to extract core modules from the legacy system and create well-defined packages for common code. Ironically, this increased the number of circular dependencies, seemingly pushing us further from our goal of autonomous, performant development experiences for product squads. Questions arose about the value and purpose of this work. With our first product successfully deployed as a microfrontend, uncertainty lingered about whether this was the intended approach for all products. The North Star helped us build and retain alignment on multiple levels which led to more precise visions and projects with specific goals that were key to our success.&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%2Fygaewrzfi6um3uitjjes.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%2Fygaewrzfi6um3uitjjes.png" alt="A diagram showing North Star in the outer circle with smaller circles labelled as local visions, and even smaller ones symbolising projects" width="800" height="838"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These concerns signaled the need to revisit and refine our vision and strategy for modularization. While we had initially articulated our goals through the North Star, those principles now felt abstract and disconnected from our actions. We committed to addressing this disconnect and reaffirming the value of our investment. We retroactively analyzed team velocity across different areas of the codebase, finding strong evidence that well-defined modules accelerated value delivery. Armed with this data, we crafted a revised strategy document that clearly outlined the scope and goals, emphasizing our expectation for a fully packaged codebase. We explicitly excluded independent deployments via Module Federation, pending further evaluation of the trade-offs. This clarification restored focus and alignment within the team.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #7:&lt;/strong&gt; &lt;em&gt;Maintaining alignment is an ongoing process, not a one-time achievement. Regularly reiterating goals and non-goals is essential throughout a project's lifecycle.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling for the Future
&lt;/h2&gt;

&lt;p&gt;After three years of hard work by the entire Hotjar frontend team, we reached a significant milestone in early 2024. Circular dependencies were eliminated, and all core modules and domain-related code were segregated into well-defined NPM packages. This effort unlocked the full potential of tooling like NX and paved the way for further improvements, such as TypeScript project references.&lt;/p&gt;

&lt;p&gt;The modularization effort finished just in time for the integration of Hotjar products with modules of our parent company, Contentsquare. This wasn’t part of our initial 2020 plans when defining our North Star, but it aligned perfectly with the scalability goals we envisioned back then. It also emphasised how a decoupled, modular architecture can allow teams to adapt seamlessly to new requirements, including swapping or reusing specific parts of the system with minimal risk.&lt;/p&gt;

&lt;p&gt;At the end of the day, this journey was more than a technical success - it was a demonstration of how thoughtful, forward-looking engineering can empower organizations to meet unforeseen challenges and deliver exceptional results. The real-world benefits of our investment demonstrated more than we could ever expect.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Takeaway #8:&lt;/strong&gt; &lt;em&gt;Plan for replaceability rather than specific, near future, scenarios. Flexible architectures open doors to opportunities you can’t yet foresee - the unknown unknowns.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;Our three-year modularization journey has been a testament to the power of perseverance, adaptability, and a shared vision. We encountered unexpected hurdles, course-corrected along the way, and ultimately transformed our front-end architecture into a more scalable and maintainable system. Here's a recap of the key lessons we learned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Long-term visions don't require detailed upfront plans:&lt;/strong&gt; Aligning on core principles is key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transparency fosters collaboration and accelerates problem-solving:&lt;/strong&gt; Share your ideas early and often.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Capitalize on opportunities to align technical initiatives with business goals:&lt;/strong&gt; Even small wins contribute to the larger vision.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid overthinking and strive for minimal viable solutions:&lt;/strong&gt; Iterate based on real-world feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define your metrics early and begin data collection immediately:&lt;/strong&gt; Early insights enable better decision-making.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;In a product-led organization, evaluate ideas through the lens of customer needs:&lt;/strong&gt; Timing is crucial.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintaining alignment is an ongoing process:&lt;/strong&gt; Regularly reiterate goals and non-goals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Plan for replaceability:&lt;/strong&gt; You can never know what the future brings.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While our journey has reached a significant milestone, it's not over. We continue to refine our modularized architecture, explore new technologies, and optimize for developer experience and performance.&lt;/p&gt;

&lt;p&gt;We're eager to hear from you! What challenges have you faced in your modularization efforts? What lessons have you learned? Share your experiences and insights in the comments below.&lt;/p&gt;

</description>
      <category>leadership</category>
      <category>architecture</category>
      <category>frontend</category>
      <category>refactorit</category>
    </item>
    <item>
      <title>How we optimized perceived performance to improve our KPIs: a Hotjar case study</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Fri, 20 Dec 2024 11:00:00 +0000</pubDate>
      <link>https://dev.to/erykpiast/how-we-optimized-perceived-performance-to-improve-our-kpis-a-hotjar-case-study-4mkp</link>
      <guid>https://dev.to/erykpiast/how-we-optimized-perceived-performance-to-improve-our-kpis-a-hotjar-case-study-4mkp</guid>
      <description>&lt;p&gt;No one likes to wait. Even at a nice restaurant, if the food takes too long to arrive, you’ll start wriggling on your chair and looking around for the waiter. Worst case, you might even leave. &lt;/p&gt;

&lt;p&gt;This is nothing new: people are impatient by nature, and your users are no different. If you ask them to wait too long for their requested content, they’ll enjoy the experience less and eventually drop off.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Perceived performance is a subjective measure of website performance.&lt;/strong&gt; It focuses on how fast a website seems to the user rather than technical metrics, and is crucial for user-centric optimization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google's Core Web Vitals help analyze and improve perceived performance&lt;/strong&gt;. For example, Largest Contentful Paint (LCP) measures when the biggest visual part of a page appears, offering a user-centric view of loading performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard metrics like LCP aren’t always an accurate reflection of user needs.&lt;/strong&gt; For instance, at Hotjar, our sign-up page's key metric is the time it takes for the ‘Sign up’ button to appear, which aligns with user intent.&lt;/li&gt;
&lt;li&gt;Rather than artificially slowing down the user experience for A/B testing, you can &lt;strong&gt;correlate loading times with conversion rates for various user groups&lt;/strong&gt;. This approach allows you to measure the real impact of performance on business KPIs and improve conversion rates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep reading to see how we did it at Hotjar (and what our results looked like). 👇&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;It’s a fact that &lt;strong&gt;performance matters&lt;/strong&gt;, and &lt;a href="https://www.sciencedirect.com/science/article/pii/S2351978915004370" rel="noopener noreferrer"&gt;multiple studies&lt;/a&gt; have confirmed it. But less defined is the true impact of user experience on your business metrics. Without this understanding, you won’t know how much to invest in optimizing loading performance. &lt;/p&gt;

&lt;p&gt;The good news is there’s a simple way to do this—and you probably already have all the tools you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you should measure (and how)
&lt;/h2&gt;

&lt;p&gt;First of all, what is ‘perceived performance’? &lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Performance/Perceived_performance" rel="noopener noreferrer"&gt;According to MDN&lt;/a&gt;, it’s a ‘subjective measure of website performance’ and ‘how fast a website seems to the user’. &lt;/p&gt;

&lt;p&gt;When we talk about perceived performance, we’re not concerned with the endpoint latency or the amount of server memory used by the backend service. &lt;strong&gt;Perceived performance metrics are, first and foremost, user-centric.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Examples of perceived performance metrics
&lt;/h3&gt;

&lt;p&gt;The most popular examples are &lt;a href="https://web.dev/vitals/" rel="noopener noreferrer"&gt;Web Vitals&lt;/a&gt;, proposed by Google. They aim to provide a unified way of measuring perceived performance across different websites. &lt;/p&gt;

&lt;p&gt;Let’s look at specific metrics from that set: one of them is the Largest Contentful Paint or LCP. The measurement starts when the user requests the page to load, for example, by clicking a link. It ends when the biggest visual part of the page, such as an image or a block of text, appears on the screen. &lt;/p&gt;

&lt;p&gt;The element that occupies the most space on the screen is considered the most important for visitors, so it’s a nice proxy metric for a website’s overall loading performance. It’s a universal approach that allows you to compare completely different web pages, from a newspaper article to an ecommerce checkout page.&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%2Ft09d7uxbgk8se4vjvtfp.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%2Ft09d7uxbgk8se4vjvtfp.png" alt="Core Web Vitals report for Hotjar's sign-up page&amp;lt;br&amp;gt;
" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  When Web Vitals don’t work
&lt;/h3&gt;

&lt;p&gt;In the world of SaaS products, we’re usually more focused on specific &lt;a href="https://www.hotjar.com/customer-journey-map" rel="noopener noreferrer"&gt;customer journeys&lt;/a&gt; and &lt;a href="https://www.hotjar.com/product-management-glossary/jobs-to-be-done" rel="noopener noreferrer"&gt;jobs to be done&lt;/a&gt; (JTBD). &lt;strong&gt;Often, it’s not the element taking the biggest space that provides the biggest value to our customers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Consider the example of Hotjar’s sign-up page: people come here to use the registration form, although most of the screen area is filled with marketing copy and customer logos on the right-hand side.&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%2Fqh216d2ga759xqaapzva.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%2Fqh216d2ga759xqaapzva.png" alt="Hotjar’s sign-up page&amp;lt;br&amp;gt;
" width="800" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We recognized that, in this scenario, the Largest Contentful Paint isn’t the best metric to focus on. So, we defined our own ‘web vital’ focused on our users and this particular use case. We decided that for our sign-up page, the key perceived performance metric is &lt;strong&gt;the time it takes for the ‘Sign up’ buttons to appear on the screen&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Thankfully, existing tech makes it easy to collect this type of data. For example, the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/PerformanceElementTiming/renderTime" rel="noopener noreferrer"&gt;Element Timing API&lt;/a&gt;, available in Chromium-based browsers, allows the measurement of arbitrary HTML elements. Shims for other browsers exist, and it’s not difficult to figure out a custom solution that works with single-page applications written in popular front-end frameworks, too.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to connect the dots
&lt;/h2&gt;

&lt;p&gt;After deciding which page element is the most important for our users, we started collecting and visualizing this data from a sample of real user sessions (Real User Monitoring or RUM). This approach enabled us to measure for the first time how long it took for each page visitor to see the ‘Sign up’ buttons. &lt;/p&gt;

&lt;p&gt;For example, the median (50th percentile) value was around 2.75 seconds, while the 75th percentile was around 5 seconds, as seen in the chart below.* This data was very informative, but it didn’t answer the question: how much should we invest in performance optimization to see positive business impact?&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%2Fj9bdg53zkvdilfpe5gwc.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%2Fj9bdg53zkvdilfpe5gwc.png" alt="A column chart showing the simulated conversion value change for the control and the variant groups of the experiment" width="800" height="504"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*Note: all numbers on charts are given for explanatory purposes. They’re not real data from the Hotjar website.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: slow it down and see what happens
&lt;/h3&gt;

&lt;p&gt;We agreed that to understand the impact of potential changes, we need to somehow correlate our business KPIs—in this case, the conversion rate of the registration form—with the performance perceived by users. &lt;/p&gt;

&lt;p&gt;One method we considered was an &lt;a href="https://www.hotjar.com/ab-testing" rel="noopener noreferrer"&gt;A/B test&lt;/a&gt;, where one group of visitors would get an artificially slowed-down experience while the other would be served as usual. By comparing conversions between these two groups, we could see the impact of the slowdown and, thus, the impact of potential speedup.&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%2Fi1r5oq6y619rp8x3xdza.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%2Fi1r5oq6y619rp8x3xdza.png" alt="A column chart showing distribution of page loading time metrics in the user population, with marked performance buckets" width="800" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An experiment like this could give us the answer we want, but it would be against our core values. At Hotjar, &lt;strong&gt;we put our customers at the heart of everything we do&lt;/strong&gt;, so purposefully making the experience worse for someone just didn’t seem right to us.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 2: divide and analyze
&lt;/h3&gt;

&lt;p&gt;Instead, we looked at the distribution histogram and realized we already have users with better or worse experiences loading the sign-up page. There are plenty of visitors for whom the page loads quickly (the left part of the graph below), but also a significant number who have pretty long loading times (the long ‘tail’ on the right). &lt;/p&gt;

&lt;p&gt;It’s not surprising if we consider that people who visit the Hotjar website from all over the world have different connection conditions and different device speeds. We could read the conversion data for different people separately and see if the loading time correlates with the business. In a way, we could treat existing website visitors as A/B testing groups.&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%2Fybef81fzw8t3bqyol88o.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%2Fybef81fzw8t3bqyol88o.png" alt="Image description" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We decided to follow this approach and started by lowering the resolution of our dataset significantly. While engineers and data analysts are comfortable talking about percentiles and distribution metrics, product owners speak a different language. It’s difficult to work with large amounts of data without referring to tricky statistical and mathematical concepts. We decided we could go with a simpler approach and still get data that we could trust while also making the communication between product and engineering easier. &lt;/p&gt;

&lt;p&gt;Instead of trying to make sense of the full histogram, we split all our users into three buckets—people with good, average, and bad loading experiences. We again followed &lt;a href="https://web.dev/defining-core-web-vitals-thresholds/" rel="noopener noreferrer"&gt;Google recommendations&lt;/a&gt; about what most people consider a ‘good experience’. In the case of a web page like a sign-up page, it makes sense to use generic values, as the use case is pretty standard—we can assume that our potential customers are similar to average internet users. In this case&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Every experience &lt;strong&gt;below 2.5 seconds&lt;/strong&gt; is considered good&lt;/li&gt;
&lt;li&gt;Every experience &lt;strong&gt;between 2.5 and 4 seconds&lt;/strong&gt; is average (a.k.a. ‘needs improvement’)&lt;/li&gt;
&lt;li&gt;Every experience &lt;strong&gt;above 4 seconds&lt;/strong&gt; is bad (a.k.a. ‘poor’)&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%2Ft06ensp7w1vv4anfo0fx.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%2Ft06ensp7w1vv4anfo0fx.png" alt="Image description" width="800" height="231"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Business opportunity and optimization
&lt;/h2&gt;

&lt;p&gt;We read conversion data for each of these buckets, and found that people for whom the sign-up page loads faster—below 2.5 seconds—are much more likely to finalize account creation. The difference between the conversion of visitors with good and bad experiences was as high as 13%. &lt;/p&gt;

&lt;p&gt;The KPI value in the average bucket was significantly lower, too. At the time, the number of people falling into good buckets vs. the average and bad buckets was pretty close to a 50/50 split.&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%2Foi0j6g51dugnnqplio4e.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%2Foi0j6g51dugnnqplio4e.png" alt="A column chart showing the conversion value for each perceived performance bucket" width="800" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntrktn0nb6fye2tz3bkg.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%2Fntrktn0nb6fye2tz3bkg.png" alt="A pie chart showing the percentage of the user population in different perceived performance buckets" width="800" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That was an amazing finding that flagged a business opportunity. We calculated that if we could improve the loading experience for all visitors having average and bad experiences enough to join the good bucket, we could improve our total conversion rate for the sign-up screen by as much as 6%! This was an upper boundary, of course. It’s practically impossible to provide every visitor with an excellent loading experience—we can only impact our system, not external conditions that affect the performance. We knew, though, that there was a lot of space for improvement in our current architecture, so it was worth trying.&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%2Fg8sgr8f1dnay6wx7wstc.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%2Fg8sgr8f1dnay6wx7wstc.png" alt="Two pie charts: One shows the percentage of the user population in different perceived performance buckets, and the conversion value in each bucket. The second one shows estimated conversion for the case when every user experiences good performance" width="800" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We decided to validate our hypothesis before groundbreaking changes and dig deep into performance optimizations. There were a few low-hanging fruits that promised good results. If the business impact we anticipated based on bucketed data were true, we would immediately start seeing it after these basic optimizations. &lt;/p&gt;

&lt;p&gt;We followed the plan and improved the loading speed to the 75th percentile by almost 2.5 seconds. We released this change to 50% of the traffic as the A/B test. In the test group, the number of people with the good experience grew by 12 percentage points, while the bad experience bucket shrunk by 10. That was expected, but what happened to the conversion data?&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%2Fbwt46isp7jtbrb4m6ccw.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%2Fbwt46isp7jtbrb4m6ccw.png" alt="Two pie charts showing the percentage of the user population in different perceived performance buckets before and after the optimization" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You may remember that the upper boundary for changes was 6% of the uplift of conversion. However, we didn’t move all the visitors to the good experience bucket, so we expected something significantly smaller.&lt;/p&gt;

&lt;p&gt;After checking the data from the experiment, we realized that the total conversion rate of the sign-up screen between the test and control groups was different by &lt;strong&gt;as much as 5%&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%2Fh2s0ioqqlky1sbqif7mk.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%2Fh2s0ioqqlky1sbqif7mk.png" alt="A column chart showing conversion value in the control group and the variant group" width="800" height="865"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That was a big surprise but also a statistically significant difference. It led us to release our optimizations to 100% of visitors and plan future optimization work to improve our business KPI even more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Measure perceived performance to improve your user experience
&lt;/h2&gt;

&lt;p&gt;Measuring the perceived performance of your product and correlating it with business KPIs can help you find opportunities to improve your metrics. The data needed for this investigation is available in your data lake or can be collected quickly. &lt;/p&gt;

&lt;p&gt;Once you identify focus areas, you can easily estimate the return on investment from performance optimization. &lt;strong&gt;The key to success is finding the right communication language between engineering and product.&lt;/strong&gt; Improving speed bit by bit will bring you the value in small iterations.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Note:&lt;/strong&gt; KPI improvements aren't the only benefit that observability can bring to your product. &lt;a href="https://www.hotjar.com/blog/observability" rel="noopener noreferrer"&gt;See more examples in a blog post written by my colleague, Clint.&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webperf</category>
      <category>performance</category>
      <category>startup</category>
      <category>product</category>
    </item>
    <item>
      <title>Temporal Dead Zone - the less obvious part and how to avoid it</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Thu, 02 Mar 2023 10:33:06 +0000</pubDate>
      <link>https://dev.to/hotjar/temporal-dead-zone-the-less-obvious-part-and-how-to-avoid-it-3k56</link>
      <guid>https://dev.to/hotjar/temporal-dead-zone-the-less-obvious-part-and-how-to-avoid-it-3k56</guid>
      <description>&lt;p&gt;&lt;code&gt;let&lt;/code&gt; and &lt;code&gt;const&lt;/code&gt; bindings in JavaScript have a property called Temporal Dead Zone (TDZ). It’s a deadly trap indeed, and often not easy to spot. I learned this the hard way some time ago when working on JavaScript bundle optimization at Hotjar.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Temporal Dead Zone?
&lt;/h2&gt;

&lt;p&gt;According to MDN:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;const&lt;/code&gt; variable is said to be in a ‘Temporal Dead Zone’ from the start of the block until code execution reaches the line where the variable is declared and initialized.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// TDZ starts at beginning of scope&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// undefined&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ReferenceError&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// End of TDZ (for foo)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, this is obvious—you cannot access a constant before you declare it. But nobody writes code like this!, you say. Moreover, ESLint will yell at you if you try. What’s the big deal, then? &lt;/p&gt;

&lt;p&gt;Consider a more complex example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;recurse&lt;/span&gt; &lt;span class="o"&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="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recurse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// throws an error, it's a TDZ for recurse&lt;/span&gt;
    &lt;span class="nf"&gt;recurse&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;Expressions in JavaScript are evaluated in a certain order. Assigning a value is an expression evaluated from right to left. That’s why &lt;code&gt;const a = 1 + 2&lt;/code&gt; equals &lt;code&gt;a === 3&lt;/code&gt;. So, in the example above, first, the IIFE is evaluated, then the recurse name is initialized. That makes the usage of recurse inside the IIFE forbidden. Even if the name can be seen earlier in the code, from the program evaluation flow, it’s no go. But, again, nobody writes code like this, right? And ESLint is smart enough to warn you if you try. What’s your problem, dude?&lt;/p&gt;

&lt;h2&gt;
  
  
  When ESLint can’t help you
&lt;/h2&gt;

&lt;p&gt;ESLint analyzes the code statically. It’s smart, but because it doesn’t understand all the nuances of the runtime flow, a code like the one below is perfectly valid for the checker.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;callCallback&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cb&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;cb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Returned function called!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;returned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;callCallback&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;returned&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// throws an error, it's a TDZ for returned&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A real-life example
&lt;/h2&gt;

&lt;p&gt;It all seems made-up: do we use patterns like this in our codebase? It turns out they’re rare, but they’re there. And they look completely innocent. &lt;/p&gt;

&lt;p&gt;Take a look at what I found in Hotjar’s codebase:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&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;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code may feel illogical, but the &lt;code&gt;subscribe&lt;/code&gt; method is a bit more complex than the &lt;code&gt;callCallback&lt;/code&gt; you could see above. It calls the callback asynchronously most of the time, but in certain cases, it does it synchronously. And this leads to a runtime error about an uninitialized variable when you try to unsubscribe inside the callback.&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%2F56juotydyt8gh2h6w94x.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%2F56juotydyt8gh2h6w94x.png" alt="A screenshot from browser DevTools showing an error trigger when a binding is accessed while in TDZ" width="697" height="56"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  But… I’ve never encountered such an error
&lt;/h2&gt;

&lt;p&gt;How could a code like this even slip into the main branch? It’s broken, and loading it in the browser or running a test would detect it immediately, right? It would—if we actually used &lt;code&gt;const&lt;/code&gt; and &lt;code&gt;let&lt;/code&gt; bindings. But for a long time, this wasn’t the case at Hotjar, and we didn’t realize it!&lt;/p&gt;

&lt;p&gt;Despite this syntax having been the norm here for years now, with several  such bindings present in our codebase, so far, all of them have been transpiled to &lt;code&gt;var&lt;/code&gt; before they reached the browser. For these, hoisting mechanism kicks in and silently evaluates the uninitialized binding to &lt;code&gt;undefined&lt;/code&gt;. If you’ve only read ‘JavaScript: The Good Parts’, you may even forget this mechanism exists.&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%2Funuek4s6zn9pz54iamml.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%2Funuek4s6zn9pz54iamml.png" alt="Illustration of two books - JavaScript: The Good Parts and JavaScript: The Definitive Guide - put on top of each other" width="500" height="299"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;em&gt;image source:&lt;/em&gt; &lt;a href="https://blog.klipse.tech/javascript/2016/09/21/valueOf-js.html" rel="noopener noreferrer"&gt;https://blog.klipse.tech/javascript/2016/09/21/valueOf-js.html&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;At Hotjar, the transpilation step was there because we were supporting IE 11 and other ‘odd, old browsers’ that didn’t understand the &lt;code&gt;const&lt;/code&gt; and &lt;code&gt;let&lt;/code&gt; syntax. It was replaced with &lt;code&gt;var&lt;/code&gt; on CI (via Babel), in a way engineers working in product squads weren’t noticing. Noone could see errors like the one above when testing the application.&lt;/p&gt;

&lt;p&gt;Things changed, though. Last year we officially dropped support for IE 11 in our product, which opened possibilities for dropping some compilation steps. It not only made our CI and development environment faster, but also had benefits for customers. ES6+ code is more terse, thus takes less time to load. The side-effect is that &lt;code&gt;const&lt;/code&gt;s and &lt;code&gt;let&lt;/code&gt;s are directly executed in the browser. So now, Hotjar engineers can, and certainly will, spot these errors occasionally. They’ve always been there, but hidden!&lt;/p&gt;
&lt;h2&gt;
  
  
  How to detect TDZ access
&lt;/h2&gt;

&lt;p&gt;In the example above, failing E2E (we use Cypress) tests for some areas of our codebase uncovered the issue. This was great, but also strange (and a bit concerning) at the same time: no other check—linter, type check, or unit tests—failed. This proves that some mistakes can be only spotted in runtime. Different test types are complementary and cannot replace each other.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to prevent TDZ access
&lt;/h2&gt;

&lt;p&gt;Use let, whenever there is a risk of accessing a binding before its initialization ends.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// first, initialize empty let binding with null&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// then, assign it &lt;/span&gt;
&lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&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;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is now even clearer and more explicit. Looking at the original version, you might not understand why we need to check if the &lt;code&gt;unsubscribe&lt;/code&gt; isn’t empty before calling it. After all, its type, according to TypeScript, would always be &lt;code&gt;() =&amp;gt; void&lt;/code&gt;, no? But as we’ve proven, this wasn’t true. After switching to &lt;code&gt;let&lt;/code&gt;, it’s obvious that, in some particular moment, this name could contain &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Watch out for Temporal Dead Zones even if ESLint doesn’t warn you! Write tests, including E2E ones. And make sure you’re&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%2Fvknr4dgsxtbz0plry5qk.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%2Fvknr4dgsxtbz0plry5qk.png" alt="🎶 Stayin’ alive 🎶 - a fragment of " width="480" height="350"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;em&gt;image source:&lt;/em&gt; &lt;a href="https://giphy.com" rel="noopener noreferrer"&gt;https://giphy.com&lt;/a&gt;)&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Feedback on my side project</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Fri, 22 Jan 2021 09:39:59 +0000</pubDate>
      <link>https://dev.to/erykpiast/feedback-on-my-side-project-5ebm</link>
      <guid>https://dev.to/erykpiast/feedback-on-my-side-project-5ebm</guid>
      <description>&lt;p&gt;Hey folks!&lt;/p&gt;

&lt;p&gt;If you use Webpack and are into website performance, I'd appreciate it if you take a look at the tool I made after hours and give me some feedback.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/" rel="noopener noreferrer"&gt;https://erykpiast.github.io/webpack-stats-explorer/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr0qbguxwbjclw2rhq0eb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fr0qbguxwbjclw2rhq0eb.png" alt="CleanShot 2021-01-21 at 23.46.48" width="800" height="455"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I highly encourage you to take the tour first as it nicely (I hope) explains the whole purpose of the app. You can also try to upload Webpack stats from your own project and see if that works for you.&lt;/p&gt;

&lt;p&gt;It's very early stage (despite I'm working on it for more than a year, there is just never time for it 😞), so there is a risk that it doesn't look great for more complex configurations. But that kind of feedback will be also very valuable!&lt;/p&gt;

&lt;p&gt;Thank you all in advance! 🙏&lt;/p&gt;

</description>
      <category>webpack</category>
      <category>performance</category>
      <category>webdev</category>
      <category>feedback</category>
    </item>
    <item>
      <title>How to use VSCode as your Git editor... only when you call Git from within VSCode</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Wed, 23 Sep 2020 19:49:37 +0000</pubDate>
      <link>https://dev.to/erykpiast/how-to-use-vscode-as-your-git-editor-only-when-you-call-git-from-within-vscode-19an</link>
      <guid>https://dev.to/erykpiast/how-to-use-vscode-as-your-git-editor-only-when-you-call-git-from-within-vscode-19an</guid>
      <description>&lt;p&gt;When I'm using &lt;code&gt;git&lt;/code&gt; command in the built-in VSCode terminal, for things like editing commit message or proceeding with interactive rebase, it spawns its own, CLI-based editor. That's regular Git behavior but it doesn't really make sense in this context. I have a full-featured editor already open! Why to open another one? It's one of that little insignificant things that annoy me from quite some time, but I never had a motivation to do anything with them. But finally I did! At least that one.&lt;/p&gt;

&lt;p&gt;Running an editor within an editor was a bit awkward thing by itself, but what really pushed me to take the action was broken keyboard shortcuts. VSCode doesn't work well with Nano (this is CLI editor I'm using) running inside the built-in terminal. It intercepts &lt;code&gt;Ctrl+K&lt;/code&gt; combination that cuts the selected line in &lt;code&gt;Nano&lt;/code&gt; so effectively such a basic feature as copy-paste doesn't work. That really breaks the whole point of interactive rebase as re-ordering commits needs copying and pasting.&lt;/p&gt;

&lt;p&gt;Configuring an editor used by Git is easy. &lt;code&gt;git config --global core.editor 'code --wait'&lt;/code&gt; should do the trick (I found &lt;a href="https://stackoverflow.com/a/36644561/1664353" rel="noopener noreferrer"&gt;the hint about a parameter required here&lt;/a&gt;). The thing is, I don't really want to get rid of Nano. It works well when I do things in a standalone terminal. So, is it even possible to set this setting conditionally, depending on where you call &lt;code&gt;git&lt;/code&gt; commands? To eat a cake and have a cake?&lt;/p&gt;

&lt;p&gt;After some digging, I found &lt;a href="https://stackoverflow.com/a/2596835/1664353" rel="noopener noreferrer"&gt;quite a surprising answer to a question I'd never ask&lt;/a&gt;, as I did it once and simlpy memoized the answer, which apparently wasn't full. It turns out, that if there is no editor set in the config, Git searches environment variables: &lt;code&gt;GIT_EDITOR&lt;/code&gt; and &lt;code&gt;EDITOR&lt;/code&gt;. The later is (at least theoretically) used by other CLI programs too, so I decided to use it.&lt;/p&gt;

&lt;h1&gt;
  
  
  How to make it work as a charm
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Open command palette (&lt;code&gt;Ctrl/Cmd + P&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;&amp;gt;Open settings JSON&lt;/code&gt; and confirm with &lt;code&gt;Enter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Add the following lines to your JSON config, adjusting the operating system name when needed
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"terminal.integrated.env.osx"&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;"EDITOR"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"code --wait"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Open &lt;code&gt;~/.gitconfig&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Remove the line containing the configuration of the editor (it was &lt;code&gt;editor = nano&lt;/code&gt; in my case)&lt;/li&gt;
&lt;li&gt;Open &lt;code&gt;~/.bashrc&lt;/code&gt;, &lt;code&gt;~/.zshrc&lt;/code&gt; or any other file configuring your shell&lt;/li&gt;
&lt;li&gt;Paste a snippet there (choose whatever editor you've had before in your Git config):
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;if&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="err"&gt;-z&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$EDITOR"&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="err"&gt;then&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;EDITOR=&lt;/span&gt;&lt;span class="s2"&gt;"nano"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;fi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Et voilà!&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%2Fi%2F3njxstfhbyjxf6lvkwzy.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%2Fi%2F3njxstfhbyjxf6lvkwzy.gif" alt="Screen recording showing how VSCode works as a git editor when git rebase -i is called from built-in VSCode terminal" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the Terminal app the same &lt;code&gt;git&lt;/code&gt; command launches Nano, as expected.&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%2Fi%2Fugto8wl5zsim6wlcz6rr.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%2Fi%2Fugto8wl5zsim6wlcz6rr.gif" alt="Screen recording showing how Nano works as a git editor when git rebase -i is called from a Terminal app" width="800" height="355"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy 😀&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>git</category>
      <category>vscode</category>
    </item>
    <item>
      <title>The story about a few imports</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Wed, 20 May 2020 17:33:47 +0000</pubDate>
      <link>https://dev.to/erykpiast/the-story-about-a-few-imports-40jm</link>
      <guid>https://dev.to/erykpiast/the-story-about-a-few-imports-40jm</guid>
      <description>&lt;p&gt;We all care about the performance of web applications that we build. We try to keep production bundles small and loading times low. That's a good thing! It definitely is for the user, who gets a great experience. But is it good for the developer? When we want the app to work fast, does it mean that creating it has to be slow? Can we still use external libraries and packages from NPM? Or do we have to write everything from scratch, counting each line of code?&lt;/p&gt;

&lt;p&gt;Making a webpage fast may seem like a sacrifice from the developer's point of view. How could you keep a JavaScript bundle &lt;a href="https://v8.dev/blog/cost-of-javascript-2019" rel="noopener noreferrer"&gt;below 50 kB&lt;/a&gt; when almost any popular library or framework takes half of that budget or even exceeds it? There is a way to find a compromise and keep both performance and easiness given by the ecosystem. Everything we need is the right set of tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  The story
&lt;/h2&gt;

&lt;p&gt;Let's say we're writing dev.to news listing. For each article fetched from API we're supposed to display a title, beginning of the content, and a list of tags. Tags are returned from the API as a string, using a comma as a separator, so there are some transformations needed to parse and render them.&lt;/p&gt;

&lt;p&gt;&lt;iframe src="https://codesandbox.io/embed/webpack-stats-explorer-playground-article-a-z3pnl?module=/src/article.jsx&amp;amp;view=split"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;

&lt;p&gt;This code is fairly simple, but it may be even more readable when written using predefined functions, like those form &lt;a href="https://www.npmjs.com/package/lodash" rel="noopener noreferrer"&gt;&lt;code&gt;lodash&lt;/code&gt;&lt;/a&gt; library. For many developers &lt;code&gt;lodash&lt;/code&gt; is the very first choice when it comes to finding a comprehensive set of useful functions speeding-up the development.&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="nx"&gt;_&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash/fp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;renderTags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isEmpty&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;styles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&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;tag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;)
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks quite neat! But there is a problem - bundle size increased from 12.5 kB to almost 94 kB 😱 Even if code quality could be considered as significantly better, such change would be unacceptable because it simply harms the user.&lt;/p&gt;

&lt;p&gt;When we dig into the production bundle in &lt;a href="https://erykpiast.github.io/webpack-stats-explorer/" rel="noopener noreferrer"&gt;Webpack Stats Explorer&lt;/a&gt;, we can see a few modules were added, but there is one that should attract our attention - &lt;code&gt;lodash.min.js&lt;/code&gt;. It takes almost 70 kB, the majority of our bundle! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules%2Clodash%2Clodash.min.js&amp;amp;i=0&amp;amp;t=1" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fx0y8bcuw45usyof4anox.png" title="Click on the screenshot to launch an interactive version of Webpack Stats Explorer" alt="Webpack Stats Explorer showing added lodash library in the final bundle" width="800" height="500"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Click on the screenshot to launch &lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules%2Clodash%2Clodash.min.js&amp;amp;i=0&amp;amp;t=1" rel="noopener noreferrer"&gt;an interactive version of Webpack Stats Explorer&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It turns out that by default, no matter how many functions we actually use from &lt;code&gt;lodash&lt;/code&gt;, the whole library is sent down to the user. How to fix it? Named imports are the answer. Instead of importing the whole &lt;code&gt;_&lt;/code&gt; object, we could specify each function we use by name. In the process called "tree shaking", Webpack will extract only the code that we need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Csrc%2Carticle.jsx&amp;amp;i=1&amp;amp;t=1" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fsxm2vv45pghpgxvel5sg.png" title="Click on the screenshot to launch an interactive version of Webpack Stats Explorer" alt="Webpack Stats Explorer showing default import changed to named" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is some issue with this solution, though. &lt;code&gt;lodash&lt;/code&gt; isn't really tree-shaking-friendly package, so by default switching to named imports changes nothing. To make it work as expected, we have to import each function from separate file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pipe&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="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash/fp&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;becomes&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="nx"&gt;pipe&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash/fp/pipe&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="nx"&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="s1"&gt;lodash/fp/map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is a huge sacrifice, isn't it? The code doesn't look concise anymore and we start relying on the internal structure of &lt;code&gt;lodash&lt;/code&gt; package instead of public API. Fortunately, instead of altering the code manually, it's enough to add a dedicated Babel plugin - &lt;a href="https://www.npmjs.com/package/babel-plugin-lodash" rel="noopener noreferrer"&gt;&lt;code&gt;babel-plugin-lodash&lt;/code&gt;&lt;/a&gt; and everything just works. We can keep using the named imports syntax.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"presets"&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;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"babel-preset-preact"&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;"plugins"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"babel-plugin-lodash"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The plugin does the trick - bundle size goes down by 34 kB. &lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules%2Clodash&amp;amp;i=2&amp;amp;t=1" rel="noopener noreferrer"&gt;Webpack Stats Explorer shows&lt;/a&gt;, that instead of one big file, the bundle contains a lot of small modules. And those are the only ones we actually need.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules%2Clodash&amp;amp;i=2&amp;amp;t=1" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Flb5n1dd6yjrk6kxun3hl.png" title="Click on the screenshot to launch an interactive version of Webpack Stats Explorer" alt="Webpack Stats Explorer showing the bundle shrinking after adding babel-plugin-lodash" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So the bundle is now 57 kB. Is that good enough? Comparing to 12,5 kB we had before - not necessarily. There is another tool that may help - &lt;a href="https://www.npmjs.com/package/lodash-webpack-plugin" rel="noopener noreferrer"&gt;&lt;code&gt;lodash-webpack-plugin&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LodashModuleReplacementPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lodash-webpack-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;WebpackEnhancedStatsPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack-enhanced-stats-plugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LodashModuleReplacementPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;currying&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WebpackEnhancedStatsPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;stats.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without any changes to the application code, it shrinks the bundle by another 23 kB. What kind of sorcery is this?! The whole trick is based on &lt;a href="https://github.com/lodash/lodash-webpack-plugin/blob/master/src/mapping.js" rel="noopener noreferrer"&gt;substituting some of the internal library functions&lt;/a&gt; with simpler alternatives or even no-ops. There are plenty of options available but as our code is fairly simple, we need nothing more than currying. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules%2Clodash&amp;amp;i=3&amp;amp;t=1" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkk474x1myf7v865hbelf.png" title="Click on the screenshot to launch an interactive version of Webpack Stats Explorer" alt="Webpack Stats Explorer showing how lodash-webpack-plugin wipes some internal modules out" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all those exertions, we managed to shrink the bundle to 34 kB - that's not bad. But it was 12,5 kB before. Is tripling the bundle size justified by better code readability and extensibility? I doubt! Fortunately, we can do better than that. &lt;code&gt;lodash&lt;/code&gt; isn't the only library containing utility functions available on NPM and definitely not the tiniest one. &lt;a href="https://nanoutils.github.io/" rel="noopener noreferrer"&gt;&lt;code&gt;nanoutils&lt;/code&gt;&lt;/a&gt; may be a very decent drop-in replacement. This library helped me a lot in my daily job and I can recommend it to all searching for a utility package that doesn't damage the user experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules&amp;amp;i=4&amp;amp;t=1" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F6754wer6swp34lf6x07z.png" title="Click on the screenshot to launch an interactive version of Webpack Stats Explorer" alt="Webpack Stats Explorer showing lodash replaced with nanoutils" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we simply remove &lt;code&gt;lodash&lt;/code&gt; with all build-time plugins and use raw &lt;code&gt;nanoutils&lt;/code&gt;, the package shrinks by 4 kB. That's already a success, but not so impressive! We can do more than that. Similarly to &lt;code&gt;lodash&lt;/code&gt;, by default &lt;code&gt;nanoutils&lt;/code&gt; isn't tree-shakeable so we can shrink the bundle even more with &lt;a href="https://www.npmjs.com/package/babel-plugin-transform-imports" rel="noopener noreferrer"&gt;a Babel plugin&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"presets"&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;"@babel/preset-env"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"babel-preset-preact"&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;"plugins"&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="s2"&gt;"babel-plugin-transform-imports"&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;"nanoutils"&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;"transform"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nanoutils/lib/${member}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"preventFullImport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="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="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, the bundle has a size no bigger than 13,26 kB. It's only 700 B increase when comparing to the very first version which doesn't use any library. That looks more like a cost we can afford to increase code quality and not feel guilty about breaking user experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://erykpiast.github.io/webpack-stats-explorer/?s=&amp;amp;u=.%2Fa%2Fa.json&amp;amp;u=.%2Fa%2Fb.json&amp;amp;u=.%2Fa%2Fc.json&amp;amp;u=.%2Fa%2Fd.json&amp;amp;u=.%2Fa%2Fe.json&amp;amp;u=.%2Fa%2Ff.json&amp;amp;u=.%2Fa%2Fg.json&amp;amp;p=main%2Cnode_modules%2Cnanoutils&amp;amp;i=5&amp;amp;t=1" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F30af3wucxc7dehy3854s.png" title="Click on the screenshot to launch an interactive version of Webpack Stats Explorer" alt="Webpack Stats Explorer showing results of applying Babel plugin enabling tree-shaking for nanoutils library" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;What lesson does the story tell to us? It is possible to have both performant (at least in terms of bundle size) and elegant code. There are a couple of things I wish you to remember.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tree-shaking and named imports
&lt;/h3&gt;

&lt;p&gt;Tree-shaking is one of the greatest ideas since the sliced bread, at least in the world of web bundlers. It's supported &lt;a href="https://webpack.js.org/guides/tree-shaking/" rel="noopener noreferrer"&gt;by Webpack&lt;/a&gt;, but also &lt;a href="https://rollupjs.org/guide/en/#tree-shaking" rel="noopener noreferrer"&gt;Rollup&lt;/a&gt; and &lt;a href="https://medium.com/@devongovett/parcel-v1-9-0-tree-shaking-2x-faster-watcher-and-more-87f2e1a70f79#4ed3" rel="noopener noreferrer"&gt;Parcel&lt;/a&gt;. To take advantage of tree-shaking, you should use named imports in favor of default one. Unless API of the library requires otherwise (ex. because it uses &lt;code&gt;this&lt;/code&gt; under the hood), always write&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;foo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of&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="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;lib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make this syntax your new default.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build and analytic tools
&lt;/h3&gt;

&lt;p&gt;A vast amount of modern libraries published to NPM is tree-shaking friendly. Unfortunately, for many of them, it isn't enabled by default. Use tools like &lt;a href="https://www.npmjs.com/package/webpack-bundle-analyzer" rel="noopener noreferrer"&gt;Webpack Bundle Analyzer&lt;/a&gt; and &lt;a href="https://erykpiast.github.io/webpack-stats-explorer" rel="noopener noreferrer"&gt;Webpack Stats Explorer&lt;/a&gt; to dig deep inside your production bundle and get to know what's exactly in it. If you find modules or pieces of code you suspect you don't need, try to use plugins like &lt;a href="https://www.npmjs.com/package/babel-plugin-transform-imports" rel="noopener noreferrer"&gt;babel-plugin-transform-imports&lt;/a&gt; to get rid of them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drop-in library replacements
&lt;/h3&gt;

&lt;p&gt;For many packages, it's easy to find significantly smaller counterparts with similar functionality and API surface. It's very often the case for utility libraries, but also view frameworks. Think of &lt;a href="https://preactjs.com/" rel="noopener noreferrer"&gt;Preact&lt;/a&gt; created to substitute &lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt;. To estimate the size of the package before adding it to your project, you can use &lt;a href="https://bundlephobia.com/" rel="noopener noreferrer"&gt;Bundlephobia&lt;/a&gt;. For some libraries, the bottom section provides a shortlist of alternatives, which is also super helpful!&lt;/p&gt;

&lt;p&gt;That's it! I hope you enjoyed the article and will have an opportunity to apply the described ideas to real web applications. Feel free to reach me in the comments if you have any questions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Disclaimer
&lt;/h2&gt;

&lt;p&gt;I am a creator of &lt;a href="https://erykpiast.github.io/webpack-stats-explorer" rel="noopener noreferrer"&gt;Webpack Stats Explorer&lt;/a&gt; - a free-to-use, open-source tool for developers who care about the performance of their web applications. Recently I also made some minor contributions to &lt;a href="https://nanoutils.github.io/" rel="noopener noreferrer"&gt;&lt;code&gt;nanoutils&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>performance</category>
      <category>webpack</category>
      <category>webperf</category>
    </item>
    <item>
      <title>A spread, a rest and empty values</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Sat, 07 Dec 2019 12:31:19 +0000</pubDate>
      <link>https://dev.to/erykpiast/a-spread-a-rest-and-empty-values-51d6</link>
      <guid>https://dev.to/erykpiast/a-spread-a-rest-and-empty-values-51d6</guid>
      <description>&lt;p&gt;Three dots syntax (&lt;code&gt;...&lt;/code&gt;) became quite popular in JavaScript world in the last years. It's used for a couple of different things: object and array spread, destructuring and rest arguments. In each case, the same part keeps tricky or at least not quite intuitive - empty values. What if you want to spread an array that appears to be &lt;code&gt;undefined&lt;/code&gt;? What about destructuring a &lt;code&gt;null&lt;/code&gt; object?&lt;/p&gt;

&lt;h2&gt;
  
  
  Object spread
&lt;/h2&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baz&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spreading an object is quite a common pattern when you want to create one object based on another. In the above example, we're creating object &lt;code&gt;foo&lt;/code&gt; by taking all the properties of &lt;code&gt;bar&lt;/code&gt;, whatever it contains, and setting one particular property &lt;code&gt;baz&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt;. What if &lt;code&gt;bar&lt;/code&gt; turns out to be &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;baz&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The answer is: nothing bad happens. The JavaScript engine handles this case and gracefully omits a spread. The same goes for &lt;code&gt;null&lt;/code&gt;, you can check it by yourself. That was easy!&lt;/p&gt;

&lt;h2&gt;
  
  
  Object destructuring
&lt;/h2&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Destructuring an object is handy when dealing with nested data structures. It allows binding property values to names in the scope of the function or the current block. In the example above two constant values are created: &lt;code&gt;baz&lt;/code&gt; equal to the value of &lt;code&gt;foo.baz&lt;/code&gt; and &lt;code&gt;bar&lt;/code&gt; containing all other properties of the object &lt;code&gt;foo&lt;/code&gt; (that's what is called "a rest"). What happens when &lt;code&gt;foo&lt;/code&gt; is an empty value?&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught TypeError: Cannot destructure property 'baz' of 'foo' as it is undefined.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, the JavaScript engine gives up and throws a &lt;code&gt;TypeError&lt;/code&gt;. The issue here is, that non-object value (and everything except &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; is an object in JavaScript), simply cannot be destructured. The issue can be resolved by adding some fallback value to the statement, so the destructuring part (the left one) always gets an object.&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This kind of error usually occurs when destructuring function arguments or nested objects. In such a case, instead of &lt;code&gt;||&lt;/code&gt; operator, we can use a default parameter syntax. A caveat here is not handling the &lt;code&gt;null&lt;/code&gt; value. Only &lt;code&gt;undefined&lt;/code&gt; will be replaced with an empty object.&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;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Array spread
&lt;/h2&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Similarly to the object, we can create an array based on the other. At first sight, the difference is only about the brackets. But when it comes to empty values...&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;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike object spread, the array spread doesn't work for &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; values. It requires anything iterable, like a string, &lt;code&gt;Map&lt;/code&gt; or, well, an array. Providing such a value as a fallback is enough to fix the issue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const foo = [ ...(bar || []), 1 ];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Array destructuring
&lt;/h2&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="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Array destructuring is no different - the destructured value must be iterable.&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;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Uncaught TypeError: foo is not iterable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Again, the remedy may be &lt;code&gt;||&lt;/code&gt; operator or the default argument value when it's about destructuring function parameters.&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="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="p"&gt;]&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="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To sum up - when it comes to destructuring things, we have to ensure there is always something to destructure, at least an empty object or array. Values like &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; are not welcome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rest arguments
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;baz&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 JavaScript, &lt;code&gt;...&lt;/code&gt; may be found in one more place - a function definition. In this context, it means: whatever comes to the function after named arguments, put it into an array. In the above example, &lt;code&gt;bar&lt;/code&gt; is a named argument of the &lt;code&gt;foo&lt;/code&gt; function and &lt;code&gt;baz&lt;/code&gt; is an array containing all the rest of values.&lt;/p&gt;

&lt;p&gt;What happens when exactly one argument comes to the function or when it's called with no parameters? Is that an issue at all?&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="nf"&gt;foo&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not! JavaScript engine always creates an array for the rest arguments. It also means that you can safely destructure this value without providing a fallback. The code below is perfectly valid and it's not going to fail even when &lt;code&gt;foo&lt;/code&gt; is called without arguments.&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;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;bar&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bar&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;
  
  
  Extra - JSX property spread
&lt;/h2&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="o"&gt;=&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="sr"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;JSX is not even a JavaScript, but it shares most of its semantics. When it comes to spreading the object on the React element, empty values behave just like for object spread. Why is that so?&lt;/p&gt;

&lt;p&gt;The code above means: create &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element with a single property &lt;code&gt;baz&lt;/code&gt; equal to &lt;code&gt;1&lt;/code&gt; and all the properties of the object &lt;code&gt;bar&lt;/code&gt;, whatever it contains. Does it sound familiar? Yes! It's nothing more than an object spread.&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;fooProps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;baz&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When compiling JSX down to JavaScript, &lt;a href="https://babeljs.io/repl#?browsers=&amp;amp;build=&amp;amp;builtIns=false&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=MYewdgzgLgBAZiEMC8MA8ATAlgNxgIwEMAvZAbwEYBfGMgOgaICcaB6APgG4AoIA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=true&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=true&amp;amp;presets=es2015%2Creact%2Cstage-2&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.7.5&amp;amp;externalPlugins=%40babel%2Fplugin-syntax-jsx%407.7.4" rel="noopener noreferrer"&gt;Babel uses&lt;/a&gt; old-fashioned &lt;code&gt;Object.assign&lt;/code&gt; function and does not create an intermediate variable, but the final effect is the same.&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;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;"&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;assign&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the answer is: &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; values are just fine when spreading on a React element. We don't need any checking or fallback values.&lt;/p&gt;

&lt;h2&gt;
  
  
  The snippet
&lt;/h2&gt;

&lt;p&gt;You may wonder what is the result of calling a function presented on the cover photo of this article.&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;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;qux&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;quux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;quuux&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;quux&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;quuux&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fails immediately on destructuring the first argument, as object destructuring requires at least an empty object. We can patch the function adding a default value for the argument.&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;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;qux&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;Now it fails on destructuring of &lt;code&gt;bar&lt;/code&gt; as it's &lt;code&gt;undefined&lt;/code&gt; by default and that's not an iterable thing for sure. Again, specifying a default value helps.&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;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;qux&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 form, the function works perfectly for &lt;code&gt;undefined&lt;/code&gt;. What about &lt;code&gt;null&lt;/code&gt;? Unfortunately, providing a fallback to both &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; requires at least &lt;code&gt;||&lt;/code&gt; operator. The function definition becomes far less concise.&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;function&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;barBaz&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;qux&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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;barBaz&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;quux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;quuux&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;quux&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;quuux&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;And that's fine only when you don't care about other falsy values like an empty string or &lt;code&gt;0&lt;/code&gt;. A more safe solution would be a ternary expression like &lt;code&gt;barBaz == null ? {} : barBaz&lt;/code&gt;. Things turn complicated.&lt;/p&gt;

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

&lt;p&gt;Be careful when using three dots syntax with values that you're not sure about, like ones that come from backend API or third party libraries. If you're about destructuring an object or array (or spreading an array), always check against &lt;code&gt;null&lt;/code&gt; and &lt;code&gt;undefined&lt;/code&gt; and provide a fallback value.&lt;/p&gt;

&lt;p&gt;In many cases, using optional chaining syntax may produce much more readable code. &lt;a href="https://dev.to/erykpiast/performance-of-optional-chaining-5bpk"&gt;Check out the performance of this syntax here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Two Bash scripts I cannot live without when working with Git</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Sat, 16 Nov 2019 12:04:17 +0000</pubDate>
      <link>https://dev.to/erykpiast/two-bash-scripts-i-cannot-live-without-when-working-with-git-44a1</link>
      <guid>https://dev.to/erykpiast/two-bash-scripts-i-cannot-live-without-when-working-with-git-44a1</guid>
      <description>&lt;p&gt;That's maybe an overstatement, but just a little. I use these two simple scripts for over five years now and I just cannot imagine to not have them in my shell. What's the fuzz about?&lt;/p&gt;

&lt;h3&gt;
  
  
  Clean untracked files safely
&lt;/h3&gt;

&lt;p&gt;There are some untracked files in your repository, probably some build artifacts or other trash, but you're not sure. You know only that there is something because you can see a little exclamation mark icon in ZSH command prompt. What are you doing?&lt;/p&gt;

&lt;p&gt;Most probably, you'll start with &lt;code&gt;git status&lt;/code&gt; to see what exactly is there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git status
?? tmp/
?? some_trash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything looks unnecessary, so you're deciding to remove it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clean &lt;span class="nt"&gt;-f&lt;/span&gt;
Removing some_trash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, what about &lt;code&gt;tmp&lt;/code&gt;? It's a directory! You didn't notice the little &lt;code&gt;/&lt;/code&gt; next to its name, so you have to repeat the command again with &lt;code&gt;-d&lt;/code&gt; switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clean &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
Removing tmp/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the repository is clean, but three commands were needed. If you're watchful enough, you need two, but that's still quite a lot of typing. I suppose you know many better ways of spending those five seconds!&lt;/p&gt;

&lt;p&gt;I do, that's why I wrote the script below. To make it available in each shell, just put it in &lt;code&gt;~/.bashrc&lt;/code&gt;, &lt;code&gt;~/.zshrc&lt;/code&gt; or similar file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;git_clean_untracked_safely &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;TO_REMOVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;git clean &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TO_REMOVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning..."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$TO_REMOVE&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Proceed?"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;select &lt;/span&gt;result &lt;span class="k"&gt;in &lt;/span&gt;Yes No&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Yes"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning in progress..."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        git clean &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All files and directories removed!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;fi
      &lt;/span&gt;&lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Everything is clean"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, how does it work?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git_clean_untracked_safely
Cleaning...

Would remove some_trash
Would remove tmp/

Proceed?
1&lt;span class="o"&gt;)&lt;/span&gt; Yes  2&lt;span class="o"&gt;)&lt;/span&gt; No
?#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You see what would be removed and if you think it's all right, you can just proceed by entering &lt;code&gt;1&lt;/code&gt;. Or decline with &lt;code&gt;2&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Proceed?
1&lt;span class="o"&gt;)&lt;/span&gt; Yes  2&lt;span class="o"&gt;)&lt;/span&gt; No
?# 1
Cleaning &lt;span class="k"&gt;in &lt;/span&gt;progress...
Removing some_trash
Removing tmp/
Cleaning finished!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing the function name would be a waste of keystrokes, so I'd recommend creating an alias, for example by adding a line to &lt;code&gt;~/.bash_aliases&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;gcl&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git_clean_untracked_safely'&lt;/span&gt;
&lt;span class="c"&gt;# Use different name if you use GNU Common Lisp interpreter&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you prefer, add a git alias.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.justclean &lt;span class="s1"&gt;'! bash -c "source ~/.bashrc &amp;amp;&amp;amp; git_clean_untracked_safely"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both commands will call the script in the same way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ gcl
$ git justclean
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clean old local branches
&lt;/h3&gt;

&lt;p&gt;How often you run &lt;code&gt;git branch -vvv&lt;/code&gt; and see dozens of local branches that you even not remember about? I bet it happened at least once! What can you do in such a case? You could start with &lt;code&gt;git remote prune origin&lt;/code&gt; to clean references to dead remote branches and then remove all local ones without the upstream. But picking each branch and removing it with &lt;code&gt;git branch -d&lt;/code&gt; (or &lt;code&gt;-D&lt;/code&gt;) doesn't sound like fun.&lt;/p&gt;

&lt;p&gt;Can we do better than that? Guess what... A script!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;function &lt;/span&gt;git_clean_local_branches &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nv"&gt;OPTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-d"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"-f"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"WARNING! Removing with force"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;OPTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"-D"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nv"&gt;TO_REMOVE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;git branch &lt;span class="nt"&gt;-r&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"{print &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; | egrep &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /dev/fd/0 &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;git branch &lt;span class="nt"&gt;-vv&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;origin&lt;span class="o"&gt;)&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"{print &lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TO_REMOVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing branches..."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="nv"&gt;$TO_REMOVE&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Proceed?"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;select &lt;/span&gt;result &lt;span class="k"&gt;in &lt;/span&gt;Yes No&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
      if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"Yes"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Removing in progress..."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TO_REMOVE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | xargs git branch &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OPTION&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; &lt;span class="s2"&gt;"0"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;
          &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Some branches was not removed, you have to do it manually!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;else
          &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"All branches removed!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;fi
      fi

      &lt;/span&gt;&lt;span class="nb"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"You have nothing to clean"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The usage looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git_clean_local_branches
Removing branches...

bugfix/foo
bugfix/bar
bugfix/baz
feature/qux
feature/tux
feature/fux

Proceed?
1&lt;span class="o"&gt;)&lt;/span&gt; Yes  2&lt;span class="o"&gt;)&lt;/span&gt; No
?#
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As with the previous script, you're able to both proceed or abort. By default, branches are removed gracefully, so the command may fail when the branch wasn't merged to the master (or at least when git says that).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git_clean_local_branches
Proceed?
1&lt;span class="o"&gt;)&lt;/span&gt; Yes  2&lt;span class="o"&gt;)&lt;/span&gt; No
?# 1
Removing &lt;span class="k"&gt;in &lt;/span&gt;progress...

Deleted branch bugfix/foo &lt;span class="o"&gt;(&lt;/span&gt;was 7ff047995&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Deleted branch feature/qux &lt;span class="o"&gt;(&lt;/span&gt;was cfad3e00c&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Deleted branch feature/tux &lt;span class="o"&gt;(&lt;/span&gt;was 6529123af&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Deleted branch feature/fux &lt;span class="o"&gt;(&lt;/span&gt;was b12ec9091&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

error: The branch &lt;span class="s1"&gt;'bugfix/bar'&lt;/span&gt; is not fully merged.
If you are sure you want to delete it, run &lt;span class="s1"&gt;'git branch -D bugfix/bar'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
error: The branch &lt;span class="s1"&gt;'bugfix/baz'&lt;/span&gt; is not fully merged.
If you are sure you want to delete it, run &lt;span class="s1"&gt;'git branch -D bugfix/baz'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

Some branches were not removed, you have to &lt;span class="k"&gt;do &lt;/span&gt;it manually!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to prune the branches anyway, use &lt;code&gt;-f&lt;/code&gt; switch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git_clean_local_branches &lt;span class="nt"&gt;-f&lt;/span&gt;
WARNING! Removing with force
Removing branches...

bugfix/bar
bugfix/baz
1&lt;span class="o"&gt;)&lt;/span&gt; Yes  2&lt;span class="o"&gt;)&lt;/span&gt; No
?# 1
Removing &lt;span class="k"&gt;in &lt;/span&gt;progress...

Deleted branch bugfix/bar &lt;span class="o"&gt;(&lt;/span&gt;was 7ff047995&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Deleted branch bugfix/baz &lt;span class="o"&gt;(&lt;/span&gt;was cfad3e00c&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

All branches removed!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both bash and git alias are available as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;glpo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'git_clean_local_branches'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.localprune &lt;span class="s1"&gt;'! bash -c "source ~/.bashrc &amp;amp;&amp;amp; git_clean_local_branches"'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Enjoy!&lt;/p&gt;




&lt;p&gt;Thanks to &lt;a class="mentioned-user" href="https://dev.to/jsn1nj4"&gt;@jsn1nj4&lt;/a&gt;, &lt;a class="mentioned-user" href="https://dev.to/tgu"&gt;@tgu&lt;/a&gt; and &lt;a class="mentioned-user" href="https://dev.to/euphnutz"&gt;@euphnutz&lt;/a&gt; for suggestions.&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Performance of optional chaining</title>
      <dc:creator>Eryk Napierała</dc:creator>
      <pubDate>Thu, 07 Nov 2019 21:57:15 +0000</pubDate>
      <link>https://dev.to/erykpiast/performance-of-optional-chaining-5bpk</link>
      <guid>https://dev.to/erykpiast/performance-of-optional-chaining-5bpk</guid>
      <description>&lt;p&gt;One of the coolest features added in &lt;a href="https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/" rel="noopener noreferrer"&gt;just announced TypeScript 3.7&lt;/a&gt; is optional chaining syntax. It promises a much shorter and more readable code for dealing with deeply nested data structures. How may this nice new feature affect the performance of your project?&lt;/p&gt;

&lt;p&gt;At first sight, optional chaining syntax can make the codebase significantly smaller. Instead of writing monstrous code like this one:&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="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;you can write this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;19 characters instead of 48. Quite concise!&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundle size
&lt;/h2&gt;

&lt;p&gt;The thing is, it's very unlikely that you'll ship the new syntax to the end-user. At the time of writing the post, the only browser supporting it is &lt;a href="https://www.chromestatus.com/feature/5668249494618112" rel="noopener noreferrer"&gt;Chrome 80&lt;/a&gt;. So, at least for now the transpilation is must-have.&lt;/p&gt;

&lt;p&gt;How does the expression above look in &lt;a href="https://www.typescriptlang.org/play/index.html?ssl=1&amp;amp;ssc=1&amp;amp;pln=1&amp;amp;pc=20#code/GYexH4DoCMEMCcpwF5QI4FcAeBuIA" rel="noopener noreferrer"&gt;plain old JavaScript&lt;/a&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_b&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_c&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's, well, far more than 19 characters, even more than 48 you could have before. To be precise, it's 172 characters! Minification decreases this number, but it's still 128 - 6 times more when compared with the source code.&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;var&lt;/span&gt; &lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;_c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_c&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;_b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;_c&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="nx"&gt;_c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fortunately, the TypeScript compiler isn't the only option we have. &lt;a href="https://babeljs.io/docs/en/babel-plugin-proposal-optional-chaining" rel="noopener noreferrer"&gt;Babel provides support for optional chaining&lt;/a&gt; as well.&lt;/p&gt;

&lt;p&gt;Let's check &lt;a href="https://babeljs.io/repl#?babili=false&amp;amp;browsers=&amp;amp;build=&amp;amp;builtIns=false&amp;amp;spec=false&amp;amp;loose=false&amp;amp;code_lz=GYexH4DoCMEMCcpwF5QI4FcAeBuIA&amp;amp;debug=false&amp;amp;forceAllTransforms=false&amp;amp;shippedProposals=false&amp;amp;circleciRepo=&amp;amp;evaluate=false&amp;amp;fileSize=false&amp;amp;timeTravel=false&amp;amp;sourceType=module&amp;amp;lineWrap=false&amp;amp;presets=&amp;amp;prettier=false&amp;amp;targets=&amp;amp;version=7.7.1&amp;amp;externalPlugins=%40babel%2Fplugin-proposal-optional-chaining%407.6.0%2Cbabel-plugin-syntax-optional-chaining%407.0.0-alpha.13" rel="noopener noreferrer"&gt;how it deals with the new syntax&lt;/a&gt;. Is it any better than TypeScript? It doesn't look like! 244 characters.&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;var&lt;/span&gt; &lt;span class="nx"&gt;_foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_foo$bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_foo$bar$baz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_foo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_foo$bar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_foo$bar&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_foo$bar$baz&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_foo$bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;_foo$bar$baz&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;_foo$bar$baz&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, after running Terser on the code, the code is smaller than minified TypeScript output - 82 characters.&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;var&lt;/span&gt; &lt;span class="nx"&gt;l&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="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;qux&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in the best scenario, we're getting around 4 characters in the final bundle for each one of the source code. How many times could you use optional chaining in a medium-sized project? 100 times? If you'd migrate to the new syntax in such a case, you've just added 3,5 kB to the final bundle. That sucks.&lt;/p&gt;

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

&lt;p&gt;Let's take a step back. Optional chaining isn't a new idea at all. Solutions for the &lt;code&gt;incredibly &amp;amp;&amp;amp; long &amp;amp;&amp;amp; double &amp;amp;&amp;amp; ampersands &amp;amp;&amp;amp; chains&lt;/code&gt; problem have already existed in the so-called userspace for quite some time. Jason Miller's  &lt;a href="https://github.com/developit/dlv" rel="noopener noreferrer"&gt;&lt;code&gt;dlv&lt;/code&gt;&lt;/a&gt; is only one among the many.&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="nf"&gt;dlv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar.baz.qux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Besides, this approach isn't as good as the new syntax, because it's not type-safe, it requires slightly more code on the call site - 25 characters. Plus, you must import the function from the library. But, how does the code look in the final bundle?&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="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar.baz.qux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What a surprise! 19 characters, that's as concise as optional chaining syntax itself.&lt;/p&gt;

&lt;p&gt;If you feel uncomfortable with strings, you can pass an array of strings to the function. Although there are more characters in both source and the final code, it may be worth doing. You will see later why.&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="nf"&gt;dlv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Implementation of the function itself takes only 101 characters after minification.&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;function&lt;/span&gt; &lt;span class="nf"&gt;d&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="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;o&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;t&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="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nx"&gt;n&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="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;&lt;span class="nx"&gt;l&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;n&lt;/span&gt;&lt;span class="o"&gt;===&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;o&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It means it's enough to use optional chaining transpiled with Babel twice and you'll get more code than with &lt;code&gt;dlv&lt;/code&gt;. So, is the new syntax no-go?&lt;/p&gt;

&lt;h2&gt;
  
  
  Parsing time
&lt;/h2&gt;

&lt;p&gt;The amount of the code affects not only downloading a file but also the time of parsing it. With &lt;a href="https://www.npmjs.com/package/estimo" rel="noopener noreferrer"&gt;estimo&lt;/a&gt;, we can estimate (😉) that value. Here are the median results of running the tool around 1000 times for all variants, each containing 100 equal optional chainings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.google.com/spreadsheets/d/17xD1LgKWQSoOYLRq-ZoMQr9s6LzwmF2i4_H39UquKAo/edit?usp=sharing" rel="noopener noreferrer"&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%2Fona0uim3rgzcchvor2gd.png" alt="code parsing time" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It seems that parsing time depends not only on the size of the code but also on the syntax used. Relatively big "old spice" variant gets significantly lower time than all the rest, even the smallest one (native optional chaining).&lt;/p&gt;

&lt;p&gt;But that's only a curiosity. As you can see, at this scale differences are negligible. All variants are parsed in time below 2 ms. It happens at most once per page load, so in practice that's a free operation. If your project contains much more optional chaining occurrences, like ten thousand, or you run the code on very slow devices - it might matter. Otherwise, well, it's probably not worth to bother.&lt;/p&gt;

&lt;h2&gt;
  
  
  Runtime performance
&lt;/h2&gt;

&lt;p&gt;Performance is not only about the bundle size, though! How fast is optional chaining when it goes to execution? The answer is: it's incredibly fast. Using the new syntax, even transpiled to ES5 code, may give 30x (!) speedup comparing to &lt;code&gt;dlv&lt;/code&gt;. If you use an array instead of a string, though, it's only 6x.&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%2Fdiuwg863d6brsnwzuo1y.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%2Fdiuwg863d6brsnwzuo1y.png" alt="jsPerf results" width="800" height="458"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No matter whether you &lt;a href="https://jsperf.com/optional-chaining-empty-object" rel="noopener noreferrer"&gt;access empty object&lt;/a&gt;, &lt;a href="https://jsperf.com/optional-chaining-full-path" rel="noopener noreferrer"&gt;full one&lt;/a&gt; or &lt;a href="https://jsperf.com/optional-chaining-null-inside" rel="noopener noreferrer"&gt;one with null inside&lt;/a&gt;, approaches not employing accessor function are far more performant.&lt;/p&gt;

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

&lt;p&gt;So, is optional chaining fast or slow? The answer is clear and not surprising: it depends. Do you need 150 M operations per second in your app? Don't choose a library. Is 25 M enough but you count each byte of your bundle size? You may consider something like &lt;a href="https://www.npmjs.com/package/dlv" rel="noopener noreferrer"&gt;&lt;code&gt;dlv&lt;/code&gt;&lt;/a&gt;. Want to have both? Ship native version to the latest browsers and use Babel to provide fallback for older ones.&lt;/p&gt;




&lt;p&gt;This post &lt;a href="https://allegro.tech/2019/11/performance-of-javascript-optional-chaining.html" rel="noopener noreferrer"&gt;Performance of JavaScript optional chaining&lt;/a&gt; appeared first on &lt;a href="https://allegro.tech/" rel="noopener noreferrer"&gt;Allegro.tech Blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>performance</category>
      <category>typescript</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
