<?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: Quave</title>
    <description>The latest articles on DEV Community by Quave (@quave).</description>
    <link>https://dev.to/quave</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%2Forganization%2Fprofile_image%2F9429%2F14d24107-7f23-4ea0-b4a2-7389a1119c20.png</url>
      <title>DEV Community: Quave</title>
      <link>https://dev.to/quave</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quave"/>
    <language>en</language>
    <item>
      <title>Building Reactive Lists with Meteor and Blaze</title>
      <dc:creator>Mateus Ragazzi</dc:creator>
      <pubDate>Tue, 23 Sep 2025 14:42:20 +0000</pubDate>
      <link>https://dev.to/quave/building-reactive-lists-with-meteor-and-blaze-54i9</link>
      <guid>https://dev.to/quave/building-reactive-lists-with-meteor-and-blaze-54i9</guid>
      <description>&lt;p&gt;Hey there! If you're diving into building real-time apps, you know how crucial it is to keep your UI up-to-date. At &lt;a href="https://quave.dev" rel="noopener noreferrer"&gt;Quave&lt;/a&gt;, we're big fans of &lt;a href="https://www.meteor.com" rel="noopener noreferrer"&gt;Meteor JS&lt;/a&gt; and its ecosystem to make this happen.&lt;/p&gt;

&lt;p&gt;One of available tools is &lt;a href="https://www.blazejs.org" rel="noopener noreferrer"&gt;Blaze JS&lt;/a&gt;, which gives you a great platform for creating real-time apps. While Blaze isn't recommended for new projects (the Meteor team now focuses on React and Vue.js), you'll likely encounter it in legacy codebases.&lt;/p&gt;

&lt;p&gt;Today, I'm gonna walk you through how to build a reliable and reactive list using Blaze's &lt;code&gt;#let&lt;/code&gt; directive. Trust me, this will save you from some async nightmares when working with existing Blaze applications!&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%2Fytt755vqprek2303v1em.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%2Fytt755vqprek2303v1em.png" alt="Reactive lists in Blaze with the #let directive made simple" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem with Lists
&lt;/h2&gt;

&lt;p&gt;Lists are everywhere in apps — user lists, task lists, etc. Keeping them updated in real-time when data changes can be tricky sometimes. You might be tempted to use a &lt;code&gt;ReactiveVar&lt;/code&gt; to manage your list's state, and that's not a bad idea. But Blaze's &lt;code&gt;#let&lt;/code&gt; directive can make your life &lt;em&gt;way&lt;/em&gt; easier by simplifying how you handle reactive data sources. Let's see how it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reactive List with &lt;code&gt;#let&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Imagine you've got a list of users you want to display, and it needs to update instantly when a new user is added or one gets updated. Instead of using &lt;code&gt;ReactiveVar&lt;/code&gt; or dealing with async and sync operations, Blaze's &lt;code&gt;#let&lt;/code&gt; lets you declaratively bind a reactive data source to a variable in your template.&lt;/p&gt;

&lt;p&gt;To achieve that, you need to set up a helper in your JavaScript to fetch the data reactively. For our user list, we'll use Meteor's &lt;code&gt;Users&lt;/code&gt; collection and sort it by creation date:&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;Template&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;myTemplate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="nf"&gt;myList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;createdAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;fetch&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;This helper gets all users from the &lt;code&gt;Users&lt;/code&gt; collection, sorted by the most recent first. The &lt;code&gt;fetch()&lt;/code&gt; method ensures we get an array of documents that Blaze can work with.&lt;/p&gt;

&lt;p&gt;Now, in your Blaze template, you use the &lt;code&gt;#let&lt;/code&gt; directive to bind this helper to a variable called &lt;code&gt;list&lt;/code&gt;. Then, you can iterate over it or check its length to display the right UI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;template&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"myTemplate"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  {{#let list=myList}}
    {{#if list.length}}
      &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
        {{#each user in list}}
          &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;{{user.name}}&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
        {{/each}}
      &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
    {{else}}
      &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;No users found.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
    {{/if}}
  {{/let}}
&lt;span class="nt"&gt;&amp;lt;/template&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's what's cool about this setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Since &lt;code&gt;myList&lt;/code&gt; is backed by a Meteor &lt;code&gt;find&lt;/code&gt; query, it's reactive. If a new user is added to the &lt;code&gt;Users&lt;/code&gt; collection, the list updates automatically, and Blaze re-renders the UI.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;#let&lt;/code&gt; directive lets you assign the helper's result to a variable (&lt;code&gt;list&lt;/code&gt;) that you can use throughout the template. It keeps your code readable and avoids messy nested helper calls.&lt;/li&gt;
&lt;li&gt;By using &lt;code&gt;list.length&lt;/code&gt;, you can easily toggle between showing the list or a "No users found" message, avoiding using a helper to do so.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Should I use &lt;code&gt;#let&lt;/code&gt; or &lt;code&gt;ReactiveVar&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;The choice between &lt;code&gt;#let&lt;/code&gt; with reactive helpers and &lt;code&gt;ReactiveVar&lt;/code&gt; often comes down to your specific use case: &lt;code&gt;#let&lt;/code&gt; excels for straightforward data binding from collections, while &lt;code&gt;ReactiveVar&lt;/code&gt; shines when you need to manage derived state or implement custom reactive logic that doesn't directly map to a database query.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;Building reactive lists doesn't have to be a nightmare, specially on legay code bases. With Meteor and Blaze's &lt;code&gt;#let&lt;/code&gt; directive, you can create real-time UIs with minimal code.&lt;/p&gt;

&lt;p&gt;Got questions or want to share your own Meteor tips? Drop a comment below, and let's keep the conversation going!&lt;/p&gt;

</description>
      <category>meteor</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>blazejs</category>
    </item>
    <item>
      <title>Understanding Lighthouse: The Metrics That Actually Matter for Performance and SEO</title>
      <dc:creator>Guilherme Araujo</dc:creator>
      <pubDate>Thu, 10 Jul 2025 12:52:43 +0000</pubDate>
      <link>https://dev.to/quave/understanding-lighthouse-the-metrics-that-actually-matter-for-performance-and-seo-3h2f</link>
      <guid>https://dev.to/quave/understanding-lighthouse-the-metrics-that-actually-matter-for-performance-and-seo-3h2f</guid>
      <description>&lt;h2&gt;
  
  
  🔍 What Is Lighthouse?
&lt;/h2&gt;

&lt;p&gt;Lighthouse is an automated tool that audits web pages and generates a report about five main categories:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance;&lt;/li&gt;
&lt;li&gt;Accessibility;&lt;/li&gt;
&lt;li&gt;Best Practices;
&lt;/li&gt;
&lt;li&gt;SEO;&lt;/li&gt;
&lt;li&gt;Progressive Web App (PWA).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, here's the Lighthouse report for dev.to's main page:&lt;br&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%2Fn3y9lunr88te68b4onn7.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%2Fn3y9lunr88te68b4onn7.png" alt="Lighthouse report for dev.to's main page" width="479" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The report gives each category a score, along with helpful suggestions for improvement. Here, we’ll focus mainly on the &lt;strong&gt;Performance&lt;/strong&gt; category — the one that impacts users the most, and is often the most important when it comes to SEO and UX.&lt;/p&gt;


&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Performance&lt;/strong&gt; score in Lighthouse is based on a set of real-world metrics, many of which are part of Google’s &lt;strong&gt;Core Web Vitals&lt;/strong&gt;. These metrics measure how your site loads and responds from the user’s point of view.&lt;/p&gt;

&lt;p&gt;Let’s break them down:&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%2Fd5ik332bja6psfz54as0.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%2Fd5ik332bja6psfz54as0.png" alt="Lighthouse report for dev.to's main page" width="634" height="328"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First Contentful Paint (FCP)&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Measures how long it takes for the first piece of content to show up on the screen, like text or an image. It gives users feedback that something is happening.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Largest Contentful Paint (LCP)&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tells you how long it takes for the main piece of content to load, usually a large image or headline. It shows when the page becomes useful.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Speed Index&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracks how quickly visible parts of the page appear during loading. Lower scores mean faster and smoother loading.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Time to Interactive (TTI)&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Measures how long the page takes to become fully usable. In summary, when a user can click or type without delays.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Total Blocking Time (TBT)&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds up how much time the browser is “too busy” to respond to user input, often due to heavy JavaScript tasks.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cumulative Layout Shift (CLS)&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tracks unexpected layout changes, like when content moves while the page is still loading. This helps prevent those annoying jumps that make users click the wrong thing.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  How Are Lighthouse Scores Calculated?
&lt;/h2&gt;

&lt;p&gt;Not all metrics count the same toward the final performance score. Metrics like &lt;strong&gt;LCP&lt;/strong&gt; and &lt;strong&gt;TBT&lt;/strong&gt; have more weight because they directly affect how smooth the site feels to real users.&lt;/p&gt;

&lt;p&gt;Lighthouse uses real-world data from tools like the HTTP Archive and Chrome UX Report to compare your site with others on the web.&lt;/p&gt;

&lt;p&gt;Here’s the current weight breakdown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Largest Contentful Paint (LCP):&lt;/strong&gt; 25%
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Total Blocking Time (TBT):&lt;/strong&gt; 30%
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cumulative Layout Shift (CLS):&lt;/strong&gt; 15%
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;First Contentful Paint (FCP):&lt;/strong&gt; 10%
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed Index:&lt;/strong&gt; 10%
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time to Interactive (TTI):&lt;/strong&gt; 10%&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  What About the Other Categories?
&lt;/h2&gt;

&lt;p&gt;Performance is super important, but the other Lighthouse categories also matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility&lt;/strong&gt; checks if your site works well for people with disabilities.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Best Practices&lt;/strong&gt; makes sure you’re following web and security standards.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;SEO&lt;/strong&gt; looks at basic on-page SEO, like titles, meta tags, and headings.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;PWA&lt;/strong&gt; checks if your site can work like an app on mobile devices.&lt;/p&gt;

&lt;p&gt;These don’t directly affect Core Web Vitals, but they’re key for overall user experience.&lt;/p&gt;


&lt;h2&gt;
  
  
  🚀 How to Improve Your Scores
&lt;/h2&gt;

&lt;p&gt;Improving your Lighthouse performance score doesn't mean rewriting your entire site, many optimizations are small tweaks that can have a big impact. Let’s dive deeper into the most effective techniques and how they help your metrics.&lt;/p&gt;
&lt;h3&gt;
  
  
  Optimize Images
&lt;/h3&gt;

&lt;p&gt;Images often make up the bulk of a page’s weight. If they’re not optimized, they can slow down your site dramatically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use modern formats like WebP or AVIF:&lt;/strong&gt; These formats offer better compression than traditional formats like JPEG or PNG, reducing file sizes without losing quality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resize images to match display sizes:&lt;/strong&gt; Don’t load a 5000px-wide image if it’s only going to show at 300px.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Metrics impacted: Largest Contentful Paint (LCP), Speed Index&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Reduce and Split Your JavaScript
&lt;/h3&gt;

&lt;p&gt;JavaScript is powerful, but too much of it can cause delays in loading and make the browser unresponsive.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bundle splitting:&lt;/strong&gt; Tools like Webpack, Vite, or Next.js allow you to split your code into smaller chunks that are loaded only when needed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tree shaking:&lt;/strong&gt; Remove unused code from third-party libraries during bundling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Avoid unnecessary frameworks or heavy dependencies:&lt;/strong&gt; Sometimes vanilla JS or lighter libraries can do the same job.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// webpack.config.js&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;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/index.js&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="na"&gt;optimization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;splitChunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;all&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Only importing what you need&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;debounce&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-es&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Instead of a full UI framework, use vanilla:&lt;/span&gt;
&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#btn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Clicked!&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;Metrics impacted: Total Blocking Time (TBT), Time to Interactive (TTI)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Use Lazy Loading
&lt;/h3&gt;

&lt;p&gt;Lazy loading defers the loading of non-critical content, like images below the fold, until the user is about to see them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;loading="lazy"&lt;/code&gt; attribute to &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; elements.&lt;/li&gt;
&lt;li&gt;For background images or custom components, you can implement lazy loading with JavaScript.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This helps reduce the initial load size, speeding up the time it takes for your page to become interactive.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"photo.jpg"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Lazy-loaded photo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;iframe&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"video.html"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/iframe&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Metrics impacted: First Contentful Paint (FCP), Speed Index, LCP&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Avoid Render-Blocking Resources
&lt;/h3&gt;

&lt;p&gt;Render-blocking resources delay how quickly the browser can start rendering the page. These usually include large CSS files or scripts loaded in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Minify and inline critical CSS&lt;/strong&gt; to reduce blocking.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defer non-essential JS&lt;/strong&gt; using the &lt;code&gt;defer&lt;/code&gt; or &lt;code&gt;async&lt;/code&gt; attributes.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;font-display: swap&lt;/code&gt; for web fonts to avoid text invisibility.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Metrics impacted: FCP, TTI, TBT&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Enable Caching
&lt;/h3&gt;

&lt;p&gt;Caching stores files locally so they don’t need to be downloaded again on future visits.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use HTTP cache headers like &lt;code&gt;Cache-Control&lt;/code&gt; or &lt;code&gt;ETag&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Serve assets with hashed filenames (e.g., &lt;code&gt;style.abc123.css&lt;/code&gt;) so browsers can cache them safely.&lt;/li&gt;
&lt;li&gt;Use a service worker (especially for PWAs) to cache offline-friendly resources.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Metrics impacted: Repeat view performance, Time to Interactive, Speed Index&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;By applying these strategies, you're not just improving your Lighthouse score, but you’re making your site smoother, more responsive, and more enjoyable for everyone who visits.&lt;/p&gt;

&lt;p&gt;Even small changes can lead to meaningful gains in user experience, which ultimately means better engagement, higher retention, and stronger SEO.&lt;/p&gt;

</description>
      <category>lighthouse</category>
      <category>webdev</category>
      <category>seo</category>
      <category>performance</category>
    </item>
    <item>
      <title>Hosting web app on your old Android phone</title>
      <dc:creator>Guilherme Araujo</dc:creator>
      <pubDate>Sun, 20 Apr 2025 22:47:36 +0000</pubDate>
      <link>https://dev.to/quave/hosting-web-app-on-your-old-android-phone-54bg</link>
      <guid>https://dev.to/quave/hosting-web-app-on-your-old-android-phone-54bg</guid>
      <description>&lt;p&gt;Recently, I was developing a simple bot to interact with a WhatsApp group. The code was pretty straightforward: a webhook that would read messages from the app and save data to a MongoDB database. As I continued working on the bot, I realized I needed to learn more about deploying such applications in real-world environments. To do this, I decided I wanted to self-host the application on a local server, like a Raspberry Pi, and started researching different models and their prices.&lt;/p&gt;

&lt;p&gt;However, to my surprise, the prices for these devices had significantly increased due to a global chip shortage and the rising demand for Raspberry Pis. After spending some time looking at the inflated prices, I grew frustrated and began considering alternative solutions. That's when it hit me: I have an old Android phone – a Galaxy S9 – which is still quite powerful and functional even by today’s standards. Since it’s an Android, I wondered, could I run Linux on it?&lt;/p&gt;

&lt;p&gt;Curious to explore this possibility, I began researching how I could run a Node.js application on my phone. Spoiler alert: it worked!&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk you through how I managed to turn my Galaxy S9 into a fully functional Node.js server using Termux. Along the way, I learned how Termux transforms an Android device into a powerful Linux environment, and how you can leverage your old phone as a local server for development or even small-scale production tasks. This setup can be incredibly useful if you’re looking to experiment or deploy projects without investing in extra hardware.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing &lt;strong&gt;Termux&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The first step is to install Termux, which is a powerful terminal emulator for Android that provides a Linux-like environment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to the Play Store on your Android phone and install &lt;strong&gt;Termux&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Opening &lt;strong&gt;Termux&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Once installed, open the Termux app, and let’s start by updating all the Termux packages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg update
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up SSH Access
&lt;/h3&gt;

&lt;p&gt;To make things easier and avoid constantly typing on the phone’s small keyboard, I decided to set up &lt;strong&gt;SSH&lt;/strong&gt; so that I could access the phone remotely from my computer.&lt;/p&gt;

&lt;p&gt;1- Install the &lt;code&gt;openssh&lt;/code&gt; package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;openssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- Start the SSH server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3- Find the Termux IP address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ifconfig
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4- Find the Termux username:&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;whoami&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5- Set a password for SSH access:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;passwd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, from your computer, you can connect to your phone via SSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &amp;lt;username&amp;gt;@&amp;lt;Termux_IP_address&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, you have a fully functional Linux environment running on your phone, and you can access it remotely via SSH.&lt;/p&gt;

&lt;p&gt;Optionally, you can install &lt;strong&gt;tmux&lt;/strong&gt;, a terminal multiplexer, which allows you to execute multiple applications in the same terminal session. I used &lt;strong&gt;tmux&lt;/strong&gt; to manage tasks like port forwarding and running the Node.js application simultaneously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting Up a Cloudflare Tunnel
&lt;/h3&gt;

&lt;p&gt;To expose your application to the internet, you can use &lt;strong&gt;Cloudflare Tunnel&lt;/strong&gt;. This will create a secure connection between your local server and the internet, making your Node.js application accessible.&lt;/p&gt;

&lt;p&gt;1- Install &lt;strong&gt;cloudflared&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;cloudflared
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- Run the following command to create a tunnel for your application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cloudflared tunnel &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the tunnel is running, you’ll be provided with a public URL that you can use to access your API from anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing Node.js
&lt;/h3&gt;

&lt;p&gt;Termux makes it incredibly easy to install &lt;strong&gt;Node.js&lt;/strong&gt; and start building applications.&lt;/p&gt;

&lt;p&gt;1- To install Node.js, simply run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg &lt;span class="nb"&gt;install &lt;/span&gt;nodejs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2- Once Node.js is installed, you can start your Node.js application and expose it to the internet using the Cloudflare tunnel we set up earlier.&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%2F04fv42mawqxv0jrmah8i.jpeg" 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%2F04fv42mawqxv0jrmah8i.jpeg" alt=" " width="720" height="1480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After completing these steps, my Node.js application was successfully running and publicly accessible, all from my old Android phone! I was able to deploy my bot, which interacted with the WhatsApp group and stored data in MongoDB, directly from my phone. This setup is surprisingly powerful and proves that you don’t need expensive hardware to host applications; an old Android phone can easily do the job.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Intercepting requests from WebView</title>
      <dc:creator>Matheus Castro</dc:creator>
      <pubDate>Fri, 28 Mar 2025 15:26:25 +0000</pubDate>
      <link>https://dev.to/quave/intercepting-requests-from-webview-1nin</link>
      <guid>https://dev.to/quave/intercepting-requests-from-webview-1nin</guid>
      <description>&lt;p&gt;Have you ever wondered how you can intercept &lt;strong&gt;ANY&lt;/strong&gt; requests that your WebView makes in both &lt;code&gt;Android&lt;/code&gt; and &lt;code&gt;iOS&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Well, turns out that this is possible and not so hard to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android
&lt;/h2&gt;

&lt;p&gt;For &lt;code&gt;Android&lt;/code&gt; it's actually pretty simple! You need to create a custom &lt;code&gt;web view client&lt;/code&gt; and override the &lt;code&gt;shouldInterceptRequest&lt;/code&gt; function. So it would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;WebView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;apply&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="n"&gt;webViewClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
        &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;WebViewClientCompat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;shouldInterceptRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebView&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;WebResourceRequest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;WebResourceResponse&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;requestHost&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;isRequestAllowed&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
                    &lt;span class="n"&gt;allowedHostsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;requestHost&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="n"&gt;isRequestAllowed&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="k"&gt;null&lt;/span&gt; &lt;span class="c1"&gt;// Here you can return null or call super.shouldInterceptRequest(view, request)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;WebResourceResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text/html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Blocked"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;emptyMap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nc"&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ByteArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
            &lt;span class="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method allows you to intercept the request and handle it as you may see fit. Remember that the &lt;code&gt;request.url.host&lt;/code&gt; is a string that contains &lt;strong&gt;only&lt;/strong&gt; the host, so no &lt;code&gt;pathname&lt;/code&gt; or &lt;code&gt;protocol&lt;/code&gt; there. If you want to get this information, you can get it from the &lt;code&gt;request.url&lt;/code&gt; object.&lt;/p&gt;

&lt;h3&gt;
  
  
  iOS
&lt;/h3&gt;

&lt;p&gt;For &lt;code&gt;iOS&lt;/code&gt; it's not so straightforward. Actually there's no way to intercept the request, so we need to resort to a little trick. You can intercept navigation requests (going from one &lt;code&gt;url&lt;/code&gt; to another), with &lt;code&gt;WKNavigationDelegate&lt;/code&gt;, but not requests made by the &lt;code&gt;javascript&lt;/code&gt; from the website, for example.&lt;/p&gt;

&lt;p&gt;To our rescue, we can use &lt;code&gt;WKContentRuleList&lt;/code&gt;, which in essence is a list that works in the same way as the &lt;a href="https://developer.apple.com/documentation/SafariServices/creating-a-content-blocker" rel="noopener noreferrer"&gt;content blockers&lt;/a&gt; from &lt;code&gt;Safari&lt;/code&gt; (the browser).&lt;/p&gt;

&lt;p&gt;We can use this content rule list to block requests to specific &lt;code&gt;urls&lt;/code&gt;. First, we need to define the rule list, compile it and add to our &lt;code&gt;webview&lt;/code&gt;. There's a gotcha, tough, we need to first block everything and then allow the domains we want (it's a trick, after all).&lt;/p&gt;

&lt;p&gt;The rule list is a simple &lt;code&gt;json array&lt;/code&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="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"trigger"&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;"url-filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;".*"&lt;/span&gt;&lt;span class="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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"block"&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="nl"&gt;"trigger"&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;"url-filter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"google.com"&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;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ignore-previous-rules"&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;The composition of the rule is pretty simple, you have a &lt;code&gt;trigger&lt;/code&gt; object, and a &lt;code&gt;action&lt;/code&gt; one.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;trigger&lt;/code&gt; key component is the &lt;code&gt;url-filter&lt;/code&gt;, which accepts a &lt;code&gt;regex&lt;/code&gt; to match the &lt;code&gt;url&lt;/code&gt;, and &lt;code&gt;.*&lt;/code&gt; will match &lt;strong&gt;all&lt;/strong&gt; &lt;code&gt;urls&lt;/code&gt;.&lt;br&gt;
The &lt;code&gt;action&lt;/code&gt; key component is the &lt;code&gt;type&lt;/code&gt;, which will define what will happen for the matched &lt;code&gt;url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can check the complete list of options for the &lt;code&gt;trigger&lt;/code&gt; &lt;a href="https://developer.apple.com/documentation/SafariServices/creating-a-content-blocker#Add-triggers-to-your-content-blocker" rel="noopener noreferrer"&gt;here&lt;/a&gt; and for the &lt;code&gt;action&lt;/code&gt; block &lt;a href="https://developer.apple.com/documentation/SafariServices/creating-a-content-blocker#Add-actions-to-your-content-blocker" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this example, we first block all requests, and then we allow only requests to &lt;code&gt;google.com&lt;/code&gt; by defining the action of type &lt;code&gt;ignore-previous-rules&lt;/code&gt;, which as the name implies, will ignore any rule that the url matched before.&lt;/p&gt;

&lt;p&gt;After having your list set up, you need to compile and apply it to the &lt;code&gt;userContentController&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;contentRulesList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"""
[
    {
                "&lt;/span&gt;&lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="s"&gt;": {
                    "&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="s"&gt;": "&lt;/span&gt;&lt;span class="o"&gt;.*&lt;/span&gt;&lt;span class="s"&gt;"
                },
                "&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="s"&gt;": {
                    "&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="s"&gt;": "&lt;/span&gt;&lt;span class="n"&gt;block&lt;/span&gt;&lt;span class="s"&gt;"
                }
    },
    {
                "&lt;/span&gt;&lt;span class="n"&gt;trigger&lt;/span&gt;&lt;span class="s"&gt;": {
                    "&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;filter&lt;/span&gt;&lt;span class="s"&gt;": "&lt;/span&gt;&lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="s"&gt;"
                },
                "&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="s"&gt;": {
                    "&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="s"&gt;": "&lt;/span&gt;&lt;span class="n"&gt;ignore&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;previous&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="s"&gt;"
                }
    }
]
"""&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;compiledContentRulesList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="kt"&gt;WKContentRuleListStore&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;compileContentRuleList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nv"&gt;forIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"MyAppList"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;encodedContentRuleList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;contentRulesList&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;webViewConfiguration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;userContentController&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;compiledContentRulesList&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;After that, you are all set!&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;Now you know how to intercept and block/allow requests for &lt;code&gt;webviews&lt;/code&gt; in both &lt;code&gt;android&lt;/code&gt; and &lt;code&gt;iOS&lt;/code&gt;.&lt;br&gt;
For &lt;code&gt;android&lt;/code&gt; it's pretty simple and straighforward, but for &lt;code&gt;iOS&lt;/code&gt; we need the &lt;code&gt;WKContentRuleList&lt;/code&gt; trick. Hopefully we will have a more direct way in the future.&lt;/p&gt;

&lt;p&gt;Thanks for reading! If you have any questions, let me know.&lt;/p&gt;

</description>
      <category>webview</category>
      <category>ios</category>
      <category>android</category>
      <category>mobile</category>
    </item>
    <item>
      <title>You should be this type of developer</title>
      <dc:creator>João Felipe</dc:creator>
      <pubDate>Thu, 13 Feb 2025 16:46:21 +0000</pubDate>
      <link>https://dev.to/quave/you-should-be-this-type-of-developer-n2i</link>
      <guid>https://dev.to/quave/you-should-be-this-type-of-developer-n2i</guid>
      <description>&lt;h2&gt;
  
  
  🎯 Introduction
&lt;/h2&gt;

&lt;p&gt;There are a lot of developer types, but this one is always a good fit for the most projects or companies.&lt;/p&gt;

&lt;p&gt;Have you ever worked with a developer who just ‘gets’ the product? Someone who doesn’t just code but also thinks about the user experience, business impact, and long-term product vision? That’s what a &lt;strong&gt;Product-Minded Developer&lt;/strong&gt; is all about.&lt;/p&gt;

&lt;p&gt;We have many benefits having this type of dev in our team:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Users&lt;/strong&gt;: Usually, they think about the user experience first, not just about tech.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Context&lt;/strong&gt;: Mainly because this dev has a lot of context about all the product, it's easier to create new features in different parts of the app.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Communication&lt;/strong&gt;: Normally, the product-minded developers have a good relation with non-tech people, probably because they make sure to have an understandable communication (E.g., if the API endpoint is down, they can explain it to the product owner not using technical terms).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Time&lt;/strong&gt;: They can save a lot of time for the team, because they can understand the product better.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For now, we'll explore some ways to be a product-minded developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧠 A little obvious, but: Understand the product
&lt;/h2&gt;

&lt;p&gt;The first step to be a product-minded developer is to understand the product.&lt;/p&gt;

&lt;p&gt;You should ask questions, and you should be curious about the product. Make sure you know what problem the product is solving, and how it's being used.&lt;/p&gt;

&lt;p&gt;Try to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem is the product solving?&lt;/li&gt;
&lt;li&gt;Who is the product for?&lt;/li&gt;
&lt;li&gt;How is the product being used?&lt;/li&gt;
&lt;li&gt;What are the main features?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: take a deep look at the landing page product, usually we have a really good summarized version of the product there.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  👥 Get to know the users
&lt;/h2&gt;

&lt;p&gt;You should know who the users of the product are, and what they expect from the product.&lt;/p&gt;

&lt;p&gt;Try to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Who are the users?

&lt;ul&gt;
&lt;li&gt;Normally, are they using the product on desktop, mobile, or both?&lt;/li&gt;
&lt;li&gt;What about the age? Are they already experienced or not?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;For example, if the product you're working on is a CRM system, probably the device more used is desktop, so we don't need to care so much about the mobile experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  ❓ New features: a new opportunity to ask questions
&lt;/h2&gt;

&lt;p&gt;When you're working on a new feature, you should ask questions about the feature.&lt;/p&gt;

&lt;p&gt;Try to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What problem is the feature solving?&lt;/li&gt;
&lt;li&gt;Why are we fixing this problem in that way and not in another?&lt;/li&gt;
&lt;li&gt;Who is the feature for?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  💼 Consider business goals, not just tech ones
&lt;/h2&gt;

&lt;p&gt;When you're working on a feature or a bug, you should consider the business goals.&lt;/p&gt;

&lt;p&gt;Try to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;This piece of code is solving a problem, but is it solving the right problem?&lt;/li&gt;
&lt;li&gt;Is this feature helping the business to achieve the goal?&lt;/li&gt;
&lt;li&gt;Is this bug affecting the business?&lt;/li&gt;
&lt;li&gt;How are we adding value to the user?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We, as developers, are normally focused on the code and some topics like performance, architecture, and so on. But, sometimes, we have choices to make, and we should consider the business goals.&lt;/p&gt;

&lt;h2&gt;
  
  
  🤝 Your product manager is your best friend
&lt;/h2&gt;

&lt;p&gt;Your product manager is your best friend. They can help you to understand the product better, and they can help you to understand the users better.&lt;/p&gt;

&lt;p&gt;Your tech lead will review your code, but usually they are not the best person to ask about the product.&lt;/p&gt;

&lt;p&gt;Before starting a new project, it would be nice if you schedule a meeting with the product manager to understand the product better and ask questions about the context/market/users/etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 Come up with suggestions
&lt;/h2&gt;

&lt;p&gt;You should come up with suggestions about the product. Think about it: you're a developer in this project/product, but you're a user in another one.&lt;/p&gt;

&lt;p&gt;Try to think as a user, and come up with suggestions about the product.&lt;/p&gt;

&lt;p&gt;For example, I was working on a feature that the visual was a form and a preview in a grid template, if the user changes the form, the preview should be updated, something like that:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fasskerou2rn0zb1bs49q.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%2Fasskerou2rn0zb1bs49q.png" alt="A two-column layout with a form and preview sections. The form takes more space on the left while preview sits on the right." width="800" height="520"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If the user tries to change a field below in the form, the preview is fixed at the top of the page, creating a bad user experience.&lt;/p&gt;

&lt;p&gt;I've suggested to the team to make the preview sticky, so it will follow the user when they scroll down the form.&lt;/p&gt;

&lt;p&gt;A simple improvement, but in a large-scale project, it can save a lot of time for the users and create a better vision for our product.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔑 Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Product-minded developers go beyond coding—they understand the product, users, and business.&lt;/li&gt;
&lt;li&gt;Asking the right questions is crucial when developing features.&lt;/li&gt;
&lt;li&gt;Collaboration with product managers leads to better decision-making.&lt;/li&gt;
&lt;li&gt;Small UX improvements can have a big impact on the overall experience.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Being a product-minded developer is more than just writing great code, it's about understanding the bigger picture. When you deeply understand the product, its users, and the business goals, you become a more valuable team member, capable of making smarter decisions and delivering better experiences.&lt;/p&gt;

&lt;p&gt;By asking the right questions, collaborating with product managers, and thinking beyond technical implementation, you can bridge the gap between development and product strategy. This mindset improves the quality of the product and makes your work more impactful and fulfilling.&lt;/p&gt;

&lt;p&gt;At the end of the day, great products aren’t just built with great code, they're built with great &lt;strong&gt;perspective&lt;/strong&gt;. So, be curious, stay engaged, and always think beyond the code! 🚀&lt;/p&gt;

&lt;p&gt;How do you apply a product-minded approach in your work? Let’s discuss in the comments!&lt;/p&gt;

&lt;h2&gt;
  
  
  📝 Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.pragmaticengineer.com/the-product-minded-engineer/" rel="noopener noreferrer"&gt;The Product-Minded Software Engineer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>career</category>
      <category>softwareengineering</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Optimizing End-to-End Testing: Strategies for Speed, Reliability, and Efficiency</title>
      <dc:creator>Guilherme Araujo</dc:creator>
      <pubDate>Mon, 27 Jan 2025 18:04:55 +0000</pubDate>
      <link>https://dev.to/quave/optimizing-end-to-end-testing-strategies-for-speed-reliability-and-efficiency-1idh</link>
      <guid>https://dev.to/quave/optimizing-end-to-end-testing-strategies-for-speed-reliability-and-efficiency-1idh</guid>
      <description>&lt;h1&gt;
  
  
  Best Practices for Efficient E2E Testing
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Quick Overview: What Are E2E Tests?
&lt;/h2&gt;

&lt;p&gt;End-to-End (E2E) tests are designed to run on top of your application, simulating real user interactions such as clicking buttons, typing input, navigating between pages, and more.&lt;/p&gt;

&lt;p&gt;These tests are extremely useful for checking that entire workflows work as expected. For example, in an eCommerce application, an E2E test could simulate a complete purchase journey: selecting a product, adding it to the cart, registering a credit card, and completing the checkout process. By testing real-world scenarios, E2E tests help ensure your application's key features work well together.&lt;/p&gt;




&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Recently, in my current project, we faced the task of restoring some old, skipped E2E tests. While working through them, we observed several opportunities for improvement. Many of these tests could be merged or rewritten in a more efficient way, and we realized that our approach of restoring the database for every test was significantly increasing the execution time.&lt;/p&gt;

&lt;p&gt;Additionally, we identified potential to parallelize some of the test scenarios, which could further optimize our testing process. These observations led us to focus on techniques to improve performance, such as merging and optimizing test cases, leveraging parallelization, and rethinking our database restoration strategy. In this article, I'll share these strategies, primarily based on my experience using Playwright as an E2E testing tool. However, these techniques are likely applicable to most E2E testing frameworks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Test Parallelization
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Parallelization Across Files
&lt;/h3&gt;

&lt;p&gt;Parallelizing tests is one of the most effective ways to speed up E2E test execution, especially as your test suite grows. By running multiple tests simultaneously, you can utilize your system's resources more efficiently and significantly reduce the total runtime.&lt;/p&gt;

&lt;p&gt;In our project, we configured Playwright to use 5 workers by default. Each worker runs a test file independently, enabling up to 5 tests to execute at the same time. Here's how we set it up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// playwright.config.js  &lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;defineConfig&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;@playwright/test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;defineConfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;  
  &lt;span class="na"&gt;workers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="p"&gt;});&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple change led to a significant improvement in test execution speed, as multiple tests could run concurrently.&lt;/p&gt;

&lt;p&gt;However, some tests required rewriting due to side effects. For instance, certain tests created or modified resources that other tests depended on. When these tests ran in parallel, their execution order sometimes caused failures, as the shared state was not properly managed.&lt;/p&gt;




&lt;h3&gt;
  
  
  Parallelizing Tests Within Files
&lt;/h3&gt;

&lt;p&gt;In addition to parallelizing tests across multiple files, we found that it’s also possible to enable parallel execution within a single test file. This approach is especially useful when you have multiple independent tests grouped in the same file and want to further optimize execution time.&lt;/p&gt;

&lt;p&gt;In Playwright, this can be achieved by wrapping your tests in &lt;code&gt;test.describe.parallel&lt;/code&gt;. All tests inside this block will execute simultaneously:&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;test&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parallel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Group of parallel tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&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;// Test logic here  &lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;  

  &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Test 2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;page&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;// Test logic here  &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;Based on our experience parallelizing tests across files, the key principles remain the same:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Avoid Side Effects:&lt;/strong&gt; Ensure that tests are fully independent and do not share or modify resources in a way that could interfere with other tests;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reuse Resources Wisely:&lt;/strong&gt; Manage shared resources, such as browser instances or test data, efficiently to prevent contention or conflicts during parallel execution.
This technique can deliver a noticeable speedup for large test files, but it also requires careful planning to maintain test reliability and avoid flaky results. By isolating tests and managing resources effectively, you can take full advantage of parallel execution within files.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Pre-Authentication
&lt;/h3&gt;

&lt;p&gt;Pre-authentication is a critical step to ensure that each test begins with a consistent, valid user session. This technique eliminates the need to repeatedly perform login actions for every test, significantly improving both speed and reliability in the tests, especially when tests are executed in parallel.&lt;/p&gt;

&lt;p&gt;The approach involves leveraging Playwright’s ability to work with &lt;code&gt;storageState&lt;/code&gt;, which stores session information (e.g., cookies, local storage) for pre-authenticated users. By restoring this state at the beginning of each test, the user is already logged in, allowing tests to focus on verifying functionality rather than navigating login flows.&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;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User View - TEST_ID#153&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newContext&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;storageState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`../.auth/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// Pre-authenticated session&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;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// User is already logged in at this point&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach guarantees that each test operates in isolation, preventing cross-contamination of session data. The pre-authenticated &lt;code&gt;storageState&lt;/code&gt; ensures that tests skip redundant login steps, saving valuable time while maintaining consistency across the suite.&lt;/p&gt;

&lt;p&gt;Of course, there are challenges to consider, such as ensuring that the &lt;code&gt;.auth/${email}.json&lt;/code&gt; files are consistently updated with valid session data. Additionally, it's important to focus on the reusability of helper functions, avoiding tightly coupled data or risky code that could compromise the maintainability and reliability of the test suite.&lt;/p&gt;




&lt;h3&gt;
  
  
  Best Practices for Efficient E2E Testing
&lt;/h3&gt;

&lt;p&gt;To maximize the efficiency of your E2E tests:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prioritize Test Isolation:&lt;/strong&gt; Ensure that tests are independent and avoid shared state wherever possible;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Minimize Dependencies:&lt;/strong&gt; Pre-authentication and reusable helpers can reduce redundancy and improve test reliability;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor Pipeline Performance:&lt;/strong&gt; Regularly review test runtimes and resource utilization to identify bottlenecks or opportunities for optimization;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Balance Coverage and Execution Time:&lt;/strong&gt; Focus on covering critical workflows without overloading the pipeline with unnecessary tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By adhering to these best practices, you can build a robust, scalable, and efficient E2E testing framework that delivers reliable results while saving time and resources.&lt;/p&gt;




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

&lt;p&gt;Optimizing E2E tests is an ongoing process that involves balancing performance, reliability, and coverage. By leveraging techniques like test parallelization, pre-authentication, and careful resource management, you can significantly improve test execution times without compromising accuracy. While challenges such as side effects and maintaining reusable code require attention, the benefits of a well-optimized E2E suite far outweigh the effort. These practices, when implemented thoughtfully, pave the way for a more efficient and reliable testing process, enabling faster delivery of high-quality software.&lt;/p&gt;

</description>
      <category>playwright</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Handling Promises with Higher-Order Functions in JavaScript</title>
      <dc:creator>Guilherme Araujo</dc:creator>
      <pubDate>Tue, 17 Dec 2024 16:45:50 +0000</pubDate>
      <link>https://dev.to/quave/handling-promises-with-higher-order-functions-in-javascript-3apk</link>
      <guid>https://dev.to/quave/handling-promises-with-higher-order-functions-in-javascript-3apk</guid>
      <description>&lt;p&gt;When working with arrays in JavaScript, you may find yourself needing to make asynchronous calls inside methods like &lt;code&gt;forEach&lt;/code&gt;, &lt;code&gt;.map&lt;/code&gt;, or similar functions. But after encountering errors or unexpected outputs, you might wonder, &lt;em&gt;Why isn’t this working as expected?&lt;/em&gt; In this article, we’ll dive into these situations and explore how to handle promises effectively within higher-order functions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is a higher-order function?
&lt;/h3&gt;

&lt;p&gt;Starting from the beginning:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A higher order function is a function that takes one or more functions as arguments, or returns a function as its result.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A simple example would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Higher-order function that returns a function&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createAdder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;y&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;addFive&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAdder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&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="nf"&gt;addFive&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; 
&lt;span class="c1"&gt;// Console output: 8&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What is a Promise?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;strong&gt;Promise&lt;/strong&gt; is an object representing a value that may not be available yet but will be resolved in the future. Promises allow you to attach handlers to manage the eventual success or failure of an asynchronous operation, making it easier to handle responses as they arrive.&lt;/p&gt;

&lt;p&gt;This pattern is especially useful in cases like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API calls&lt;/strong&gt;: When fetching data from external sources, such as RESTful APIs, the data may take time to arrive;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database interactions&lt;/strong&gt;: Querying a database can also be time-consuming, so promises allow you to handle the data only once it’s available.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Instead of returning the final data instantly, these asynchronous functions return a promise, which acts as a placeholder for the value that will be provided later.&lt;/p&gt;

&lt;p&gt;Given these two concepts, let’s see how they interact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Combining Promises with Higher-Order Functions: Practical Examples
&lt;/h3&gt;

&lt;p&gt;Now that we understand promises, let’s look at a common use case: fetching data, such as retrieving user information from an array of user IDs:&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;userIds&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Simulated async function to fetch user data&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`User &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; data`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&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;&lt;strong&gt;❌ The Common Mistake: Not Handling the Array of Promises Properly&lt;/strong&gt;&lt;br&gt;
When using &lt;code&gt;.map&lt;/code&gt; with an asynchronous function, a common mistake is forgetting that map will immediately return an array of promises instead of resolved values. Without additional handling, this leads to unexpected results:&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;wrongWay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This creates an array of promises, not resolved values!&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userIds&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="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="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;users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: [Promise, Promise, Promise]&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 example, each call to fetchUser(id) returns a promise, so map returns an array of promises rather than the final data values. Simply awaiting inside &lt;code&gt;.map&lt;/code&gt; doesn’t resolve the array itself; instead, it just creates a promise for each item in the array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ The Correct Approach: Using Promise.all with map&lt;/strong&gt;&lt;br&gt;
To properly handle an array of promises, you need to use Promise.all. This method takes an array of promises and returns a new promise that resolves once all the promises in the array are fulfilled. Here’s how to use Promise.all to get the resolved data:&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;rightWay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;userIds&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;id&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;fetchUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="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;users&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: ["User 1 data", "User 2 data", "User 3 data"]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  .filter function
&lt;/h3&gt;

&lt;p&gt;Like &lt;code&gt;.map&lt;/code&gt;, the &lt;code&gt;.filter&lt;/code&gt; function can lead to unexpected behavior when combined with asynchronous functions. Suppose we want to filter an array of user IDs to retrieve only the IDs of users that meet certain criteria, such as users with IDs that are "active."&lt;/p&gt;

&lt;p&gt;Let’s take a look at a common mistake and how to avoid it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;❌ The Common Mistake: Using .filter with Async Functions&lt;/strong&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;isActiveUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// Simulates checking if a user is active&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;2&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="c1"&gt;// Returns true for even IDs, false for odd ones&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&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;wrongFilterUsage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userIds&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;isActiveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="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;activeUsers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: [1, 2, 3] — the original array, not the filtered results&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 example, &lt;code&gt;.filter&lt;/code&gt; doesn’t wait for &lt;code&gt;isActiveUser(id)&lt;/code&gt; to resolve, so it operates on the original array of IDs rather than the resolved results. The array &lt;code&gt;activeUsers&lt;/code&gt; ends up containing all user IDs, regardless of whether they are active, because the async function inside &lt;code&gt;.filter&lt;/code&gt; doesn’t return the expected boolean.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;✅ The Correct Approach: Using Promise.all with .map and .filter&lt;/strong&gt;&lt;br&gt;
To properly filter an array with an async function, we need to use &lt;code&gt;.map&lt;/code&gt; in combination with &lt;code&gt;Promise.all&lt;/code&gt; to resolve all promises first, then apply &lt;code&gt;.filter&lt;/code&gt; on the resolved values. Here’s the correct approach:&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;correctFilterUsage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Use Promise.all to resolve the async checks for each user ID&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;userIds&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="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;isActiveUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Filter based on the resolved isActive status&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isActive&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="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;activeUsers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: [2] — only active user IDs&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;💡Promise.all&lt;/strong&gt;&lt;br&gt;
Using Promise.all offers several advantages for handling asynchronous tasks, especially when working with multiple promises simultaneously: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Parallel Execution of Requests&lt;/strong&gt;: One of the primary benefits of Promise.all is its ability to execute multiple promises in parallel. When you pass an array of promises to &lt;code&gt;Promise.all&lt;/code&gt;, it initiates all of them at the same time, allowing each promise to start processing concurrently. This can greatly improve performance by reducing the overall wait time, as all tasks are "in progress" together.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Example: If you’re fetching data for multiple users or making multiple API requests, Promise.all allows you to start each request without waiting for others to finish. This is particularly valuable for tasks where the order of execution doesn’t matter, like retrieving data from different sources.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Caution&lt;/strong&gt;: Use &lt;code&gt;Promise.all&lt;/code&gt; carefully, as parallel execution may not always be ideal. For cases where tasks depend on one another or need to run sequentially (e.g., writing to a database in a specific order), parallel execution may cause issues. We’ll explore sequential handling methods later on.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simplified Error Handling&lt;/strong&gt;: Another powerful feature of &lt;code&gt;Promise.all&lt;/code&gt; is its error-handling mechanism. If any promise in the array rejects, &lt;code&gt;Promise.all&lt;/code&gt; will immediately reject with that error, allowing you to handle failures in a single &lt;code&gt;.catch&lt;/code&gt; block. This makes error management more streamlined and efficient.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Only the first rejection triggers the error; the remaining promises will continue to execute but won’t trigger further &lt;code&gt;.catch&lt;/code&gt; blocks. If you need to capture each promise's result regardless of failure, consider using &lt;code&gt;Promise.allSettled&lt;/code&gt;, which returns an array of outcomes for each promise, detailing whether they resolved or rejected.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Aggregated Results&lt;/strong&gt;: Once all promises in &lt;code&gt;Promise.all&lt;/code&gt; resolve, it returns an array of the resolved values, &lt;u&gt;matching the order of the original promises&lt;/u&gt;. This makes it easy to work with the final results in a predictable way, even though each promise completes independently.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Summary Table
&lt;/h3&gt;

&lt;p&gt;There are a lot of higher-order functions available in JavaScript, but here is a attempt to create a cheat sheet to make it easier to remember how the most common functions deal with asynchronous operations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Method&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Common Issue&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Correct Solution&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.filter&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Returns array of promises&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Promise.all&lt;/code&gt; with &lt;code&gt;.map&lt;/code&gt;, then filter synchronously&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.forEach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Doesn’t wait for async functions to complete&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;Promise.all&lt;/code&gt; with &lt;code&gt;.map&lt;/code&gt; (parallel) or &lt;code&gt;for...of&lt;/code&gt; with &lt;code&gt;await&lt;/code&gt; (sequential)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.find&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Doesn’t wait for async condition to resolve&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;for...of&lt;/code&gt; with &lt;code&gt;await&lt;/code&gt; to find the first match sequentially&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.reduce&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Returns promise for each iteration&lt;/td&gt;
&lt;td&gt;Use async &lt;code&gt;reduce&lt;/code&gt;, awaiting the accumulator before each iteration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  for...of for Sequential Async Processing
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, simultaneous processing isn’t always ideal, especially when tasks depend on each other or could overload a server or API. In such cases, handling tasks sequentially can be beneficial, as it ensures that each task completes before the next one begins.&lt;/p&gt;

&lt;p&gt;This is where the &lt;code&gt;for...of&lt;/code&gt; loop becomes valuable. Unlike &lt;code&gt;.map&lt;/code&gt; or &lt;code&gt;.forEach&lt;/code&gt;, &lt;code&gt;for...of&lt;/code&gt; allows you to &lt;code&gt;await&lt;/code&gt; each asynchronous operation in sequence, ensuring that each task completes before moving on to the next. This approach is particularly useful in situations like making API calls that should occur in a specific order, processing a list of database writes sequentially, or handling rate-limited operations where triggering requests all at once could lead to errors.&lt;/p&gt;

&lt;p&gt;An example of &lt;code&gt;for...of&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;userIds&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="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="c1"&gt;// Async function that simulates an API call&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchUserData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&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;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`User &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; data`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&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;fetchAllUserData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userData&lt;/span&gt; &lt;span class="o"&gt;=&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;userIds&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchUserData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Each call waits for the previous one to complete&lt;/span&gt;
    &lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Output: ["User 1 data", "User 2 data", "User 3 data"]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nf"&gt;fetchAllUserData&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 example, each &lt;code&gt;fetchUserData&lt;/code&gt; call only starts after the previous one finishes, ensuring the operations are processed sequentially.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Everything up
&lt;/h2&gt;

&lt;p&gt;Working with asynchronous operations inside &lt;strong&gt;higher-order&lt;/strong&gt; functions can be tricky and often leads to misunderstandings and unexpected results. Each method—whether &lt;code&gt;.map&lt;/code&gt;, &lt;code&gt;.filter&lt;/code&gt;, &lt;code&gt;.forEach&lt;/code&gt;, &lt;code&gt;.find&lt;/code&gt;, or &lt;code&gt;.reduce&lt;/code&gt; —behaves differently with async functions, and recognizing these nuances is key to avoiding common mistakes. By understanding how each function handles promises and when to use tools like &lt;code&gt;Promise.all&lt;/code&gt; or the &lt;code&gt;for...of&lt;/code&gt; loop, you can significantly improve both the readability and reliability of your asynchronous code.&lt;/p&gt;

&lt;p&gt;Hopefully, this guide will help clarify any questions that arise about how functions and promises behave in different scenarios.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Common Mistakes of a Developer Starting with Meteor.js</title>
      <dc:creator>Guilherme Araujo</dc:creator>
      <pubDate>Mon, 28 Oct 2024 20:45:42 +0000</pubDate>
      <link>https://dev.to/quave/common-mistakes-of-a-developer-starting-with-meteorjs-4775</link>
      <guid>https://dev.to/quave/common-mistakes-of-a-developer-starting-with-meteorjs-4775</guid>
      <description>&lt;p&gt;After nearly two months of working with Meteor.js, I find it valuable to document some of the most common mistakes I've made while using the framework. In this article, I’ll delve into the typical errors and questions I've encountered from fellow beginners, aiming to provide insights that can help others avoid the same pitfalls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Poor optimized and unsafe Publications
&lt;/h2&gt;

&lt;p&gt;On my first publication I've created a code like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Mongo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;events&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;events.list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the example above, we create a collection called events and then declare a publication called events.list that returns a &lt;a href="https://v3-docs.meteor.com/api/collections.html#mongo-cursor" rel="noopener noreferrer"&gt;collection cursor&lt;/a&gt; to the &lt;strong&gt;entire collection with all its fields&lt;/strong&gt;. Once the client subscribes to it, all this data will be sent to the client and will be monitored for any updates that occur in the collection.&lt;/p&gt;

&lt;p&gt;Although it might seem like magic, this reactivity comes with a significant computational cost. The system has to &lt;strong&gt;monitor the entire collection, compare the differences between new and old data, and update the client with each change.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Optimizing your Publication
&lt;/h3&gt;

&lt;p&gt;To create performatic and optimized queries we need to understand some concepts about the publications we create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Use Filters to Retrieve Relevant Data:&lt;/strong&gt; Applying filters in your &lt;code&gt;find({ })&lt;/code&gt; queries is &lt;strong&gt;crucial&lt;/strong&gt; for ensuring that only relevant data is retrieved from the database. Filters allow you to precisely define the subset of data you want to fetch, based on conditions such as user ID, date range, or any other criteria. By doing so, you avoid retrieving unnecessary or irrelevant records (e.g., data belonging to other users or outdated entries), which could unnecessarily increase the amount of data sent to the client.&lt;br&gt;
Well-designed filters not only enhance security by ensuring that users only receive their own data but also reduce both server load and network traffic by minimizing the data processed and transmitted;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Leverage Projections for Fine-Grained Control:&lt;/strong&gt; Projections allow you to retrieve only the specific fields you need from your documents, instead of sending the entire document to the client. This prevents large, unnecessary objects from being transferred and reduces memory overhead on the client-side. By sending only the essential fields, you also optimize memory usage and minimize the load on the client's processing.&lt;br&gt;
For example, if you only need the name and date of the event, you can write:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;events.list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; 
  &lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&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="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Paginate Large Data Sets:&lt;/strong&gt; If you are working with large data sets, consider implementing pagination. This allows you to send data in smaller, more manageable chunks, improving performance and providing a smoother user experience;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security - Protect Sensitive Data:&lt;/strong&gt; Publishing entire collections without any restrictions can unintentionally expose sensitive data from your application. This is a significant security risk. By applying filters and projections, you can control exactly what data is sent to the client, ensuring that only the necessary and non-sensitive information is exposed. This practice not only optimizes performance but also safeguards your application by preventing unintended data leaks.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After passing thru all these concepts, now we can refactor our initial subscription:&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;check&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;meteor/check&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;events.list&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&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="o"&gt;!&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&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="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ready&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Stop publication if the user is not authenticated&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&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;page&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;const&lt;/span&gt; &lt;span class="nx"&gt;LIMIT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Calculate how many documents to skip based on the page number&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;skip&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;page&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="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;LIMIT&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;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// filter by current user's events&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&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="na"&gt;date&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="c1"&gt;// only return necessary fields&lt;/span&gt;
      &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;LIMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                 &lt;span class="c1"&gt;// limit the number of documents returned&lt;/span&gt;
      &lt;span class="na"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;skip&lt;/span&gt;                    &lt;span class="c1"&gt;// skip documents based on page number&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Too much reactivity - useSubscribe everywhere
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, leveraging subscriptions in Meteor.js can feel like magic, providing you with reactive data on the front-end with minimal effort. However, over-relying on this feature can lead to messy, hard-to-debug, and inconsistent code.&lt;/p&gt;

&lt;p&gt;When a document is published by the back-end and you subscribe to it on the front-end, the data is stored in a client-side in-memory database called &lt;a href="https://github.com/mWater/minimongo" rel="noopener noreferrer"&gt;minimongo&lt;/a&gt;. This allows you to easily access data using &lt;code&gt;Collection.find({})&lt;/code&gt; or &lt;code&gt;Collection.findOne({})&lt;/code&gt;. But as your project scales and multiple components manage the same data, this can become complicated.&lt;/p&gt;

&lt;p&gt;For instance, consider a details page that follows a listing. If your subscription is placed within the listing component and a user navigates directly to the details page via the URL, the required data may not be available for display.&lt;/p&gt;

&lt;p&gt;To avoid this issue, it’s essential to understand where data is being used and to evaluate whether it truly needs to be reactive. Using Meteor methods allows you to fetch data like a standard HTTP call from your back-end, eliminating the guesswork about data availability. This approach not only simplifies data management but also enhances performance by reducing unnecessary reactivity, leading to cleaner and more maintainable code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data Merge Issues
&lt;/h3&gt;

&lt;p&gt;One common challenge in Meteor.js arises from managing multiple subscriptions, especially when dealing with overlapping data. This can often occur due to an excessive number of subscriptions or a misunderstanding of how publications work.&lt;/p&gt;

&lt;p&gt;Consider the following example, where two subscriptions pull different fields from the same collection:&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;return&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&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="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;fields&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;host.name&lt;/span&gt;&lt;span class="dl"&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the client side, Meteor only maintains a single copy of each document, regardless of how many subscriptions exist. The server determines which fields to send to the client using a mechanism called the 'MergeBox'. Ideally, with both of these subscriptions active, the client would have the complete &lt;code&gt;host&lt;/code&gt; data, since one subscription provides the full &lt;code&gt;host&lt;/code&gt; object and the other supplies only a partial view.&lt;/p&gt;

&lt;p&gt;However, the MergeBox operates only at the top field level, as explained in Meteor's documentation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If more than one subscription sends conflicting values for a field (same collection name, document ID, and field name), then the value on the client will be one of the published values, chosen arbitrarily.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means documents aren’t deeply merged. Sometimes, the full host from the first publication will be displayed, while other times, the client may receive only the partial host.name from the second publication, leading to inconsistent data on the front-end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sorting in Publications
&lt;/h2&gt;

&lt;p&gt;When a client subscribes to a publication, the data it receives is merged with existing data in the client’s minimongo store. This merging process doesn’t always respect the original sorting order from the server, which can result in records being displayed in an incorrect order.&lt;/p&gt;

&lt;p&gt;To avoid this, it's often unnecessary to handle sorting within the publication itself unless the order of the data is dynamic (e.g., for pagination or live updates). Instead, you can delegate sorting to the front-end when retrieving data from minimongo. This ensures the data is always presented in the desired order without risking a mix-up due to how subscriptions are processed.&lt;/p&gt;

&lt;p&gt;For example, you can apply sorting directly when querying your local collection on the client:&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;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Working with &lt;code&gt;Meteor.js&lt;/code&gt; offers a powerful and flexible way to build &lt;strong&gt;real-time applications&lt;/strong&gt;, but like any framework, it comes with its own set of challenges. Through my experience, I’ve learned that understanding the inner workings of &lt;u&gt;subscriptions, data reactivity, and minimongo&lt;/u&gt; is crucial for avoiding common pitfalls. Over-reliance on reactivity can lead to performance issues, and managing multiple subscriptions without proper planning can cause data inconsistencies.&lt;/p&gt;

&lt;p&gt;By taking control of data flow—whether by strategically using Meteor methods for non-reactive data, managing data subscriptions at higher levels, or handling sorting on the client side—you can ensure cleaner, more maintainable code. With careful consideration of these practices, your Meteor.js projects will not only perform better but also be easier to debug and scale.&lt;/p&gt;

&lt;p&gt;Special thanks to my teammates João Ladeira, Isaque, and Renan for helping me understand these concepts and rescuing me from these pitfalls.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Impostor Syndrome Doesn’t Define You, Dev</title>
      <dc:creator>João Felipe</dc:creator>
      <pubDate>Fri, 25 Oct 2024 13:44:33 +0000</pubDate>
      <link>https://dev.to/quave/impostor-syndrome-doesnt-define-you-dev-27c5</link>
      <guid>https://dev.to/quave/impostor-syndrome-doesnt-define-you-dev-27c5</guid>
      <description>&lt;p&gt;If you're a developer, you've probably experienced moments when you lack confidence in making decisions or feel like you don't deserve to be where you are.&lt;/p&gt;

&lt;p&gt;This feeling has a name: &lt;strong&gt;Impostor Syndrome.&lt;/strong&gt; In this post, we'll explore some ways to deal with it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Have a mentor
&lt;/h2&gt;

&lt;p&gt;First of all, it would be really cool if you had a mentor, someone with more experience than you, probably some senior developer or tech lead from your company.&lt;/p&gt;

&lt;p&gt;A mentor can guide you with the &lt;strong&gt;best practices&lt;/strong&gt;, technically and non-technically (like communication or leadership).&lt;/p&gt;

&lt;p&gt;With their experience, they can help you &lt;strong&gt;identify where you're doing well, where you're struggling, and where to focus your efforts.&lt;/strong&gt; And this is awesome for you, if you know it, it's much easier to take the right steps to grow your skills and become a better developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understand what they expect from you
&lt;/h2&gt;

&lt;p&gt;You have a mentor, right? Now, you need to understand what is expected from you. To do this, my best advice is a little bit obvious but efficient: &lt;strong&gt;ask it.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"What do you expect from me?"&lt;/li&gt;
&lt;li&gt;"In 6 months, what do I need to achieve for you to be sure that hiring me was worth it?"&lt;/li&gt;
&lt;li&gt;"What are the characteristics you expect me to demonstrate in my daily work?"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can schedule a call with your mentor/leader and ask some questions like that. You'll demonstrate interest, and you'll have the details of expectations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Come up with a plan
&lt;/h2&gt;

&lt;p&gt;You already know what they want from you, it's time to &lt;strong&gt;create a plan&lt;/strong&gt; to achieve it.&lt;/p&gt;

&lt;p&gt;There are a lot of ways to create this plan, you can select some books to read or even define some goals for your week.&lt;/p&gt;

&lt;p&gt;But my main advice is: &lt;strong&gt;be practical.&lt;/strong&gt; Create some tasks based on what they literally expected from you.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A communicative developer? Create reports every month to your leader with your best tasks.&lt;/li&gt;
&lt;li&gt;A programmer addicted to learning/curious? Write some posts about your learning and share them with your coworkers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a nutshell, &lt;strong&gt;create practical tasks&lt;/strong&gt; to achieve what they expect from you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You'll not achieve all these skills overnight, relax and enjoy the process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Ask for feedback, A LOT
&lt;/h2&gt;

&lt;p&gt;How will you know if you are getting better or not? Feedback!&lt;/p&gt;

&lt;p&gt;Seriously, &lt;strong&gt;feedback will be your best friend&lt;/strong&gt; in dealing with imposter syndrome and being more confident. With it, you'll receive some compliments (it'll increase your confidence) and some negative reviews, which are ok, nobody is perfect.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"But how regularly should I ask for feedback?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In my humble opinion: &lt;strong&gt;the more, the better&lt;/strong&gt;. If you can ask it every single day, ask it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create something new
&lt;/h2&gt;

&lt;p&gt;Sometimes, when you work on the same project/product for a long time, you'll feel that you are not learning or improving.&lt;/p&gt;

&lt;p&gt;You'll say to yourself: "I'm doing the same thing every single day, it is not making me better.".&lt;/p&gt;

&lt;p&gt;To keep learning and FEEL that you are learning, my advice is to &lt;strong&gt;create some personal projects.&lt;/strong&gt; You can test that tool that you thought was nice, but your company does not use it.&lt;/p&gt;

&lt;p&gt;Share this knowledge with the community, You can create a post on LinkedIn about your project and describe what you learned.&lt;/p&gt;

&lt;h2&gt;
  
  
  Be careful with AI
&lt;/h2&gt;

&lt;p&gt;The AI is here to help our lives, Cursor is a good example. But be careful about it, tools like these &lt;strong&gt;can affect your confidence.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you ever felt indestructible when you finished a very hard task? Or when you finally discovered the solution to that problem, for example. Actions like these increase our confidence because they show that we can solve problems and create nice things.&lt;/p&gt;

&lt;p&gt;You MUST practice your code skills, whether formatting some complex data, creating a more reusable component for FE, or learning what you should do to reduce the API response time. You must know how to solve these problems, not just how to create the best prompt to get the solution.&lt;/p&gt;

&lt;p&gt;Summing up, use AI for productivity and automation, but &lt;strong&gt;not as a crutch.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;To cut a long story short, &lt;strong&gt;this article described 6 topics&lt;/strong&gt; that can help you to feel more confidence and improve your skills.&lt;/p&gt;

&lt;p&gt;Moreover, the text makes it clear that &lt;strong&gt;you are not alone&lt;/strong&gt;, you are not the first, and you'll not be the last who feels like a fraud.&lt;/p&gt;

&lt;p&gt;Finally, feel free to &lt;strong&gt;share more ways&lt;/strong&gt; to deal with this feeling. &lt;/p&gt;

</description>
      <category>career</category>
      <category>programming</category>
      <category>discuss</category>
      <category>community</category>
    </item>
    <item>
      <title>Configurando Caddy: Arquivos Estáticos e Autenticação Básica atrás de um Load Balancer</title>
      <dc:creator>Edimar Cardoso</dc:creator>
      <pubDate>Thu, 03 Oct 2024 19:09:11 +0000</pubDate>
      <link>https://dev.to/quave/configurando-caddy-arquivos-estaticos-e-autenticacao-basica-atras-de-um-load-balancer-3hn9</link>
      <guid>https://dev.to/quave/configurando-caddy-arquivos-estaticos-e-autenticacao-basica-atras-de-um-load-balancer-3hn9</guid>
      <description>&lt;p&gt;O &lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy server&lt;/a&gt; tem se mostrado um projeto muito promissor. Além de ser rápido, tem muitas funcionalidades "built-in" que facilitam muito a configuração. Um dos exemplos é a emissão automática de certificados SSL. Além disso, é muito fácil configurar um serviço utilizando ele.&lt;/p&gt;

&lt;p&gt;Esta semana, precisei criar um serviço para publicar arquivos estáticos. Ao tentar fazer isto com a configuração padrão dele, notei que não seria possível usar a configuração padrão por ter um cenário um pouco diferente.&lt;/p&gt;

&lt;p&gt;No meu cenário, o serviço iria rodar atrás de um load balancer na &lt;a href="https://www.zcloud.ws" rel="noopener noreferrer"&gt;zCloud&lt;/a&gt; que já é responsável por gerar os certificados e fazer a terminação do TLS/SSL. A configuração padrão gerou problemas pois não deveria gerar certificado SSL, apenas servir os arquivos na porta HTTP padrão.&lt;/p&gt;

&lt;p&gt;Além disso, havia outra particularidade: precisava de um endpoint de health check com acesso público, enquanto todo o restante precisava ser bloqueado utilizando autenticação padrão "Basic Auth".&lt;/p&gt;

&lt;p&gt;A seguir, o arquivo utilizado para fazer isto de uma forma bem simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  auto_https off
}
:8080 {
    route /_health* {
        respond 200
    }
    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicando um pouco sobre a configuração:&lt;/p&gt;

&lt;h2&gt;
  
  
  Desabilitando SSL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auto_https off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Desabilita a emissão automática de certificado e desabilita o redirecionamento para HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porta
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:8080 {
# ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porta que será aberta para receber conexões.&lt;/p&gt;

&lt;h2&gt;
  
  
  Health check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route /_health* {
        respond 200
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rota para acesso ao health check com acesso público.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arquivos estáticos com autenticação
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta parte da configuração irá receber todo restante das requests, interceptar as requisições e forçar um login, caso o usuário não esteja logado, e servir os arquivos do diretório &lt;code&gt;/var/www/static&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O valor &lt;code&gt;USER_NAME&lt;/code&gt; é o nome do usuário que será usado para autenticar.&lt;br&gt;
O valor &lt;code&gt;HASH_PASSWORD&lt;/code&gt; é o hash gerado pelo CLI do Caddy com base na senha definida. Para gerar este valor, basta seguir as instruções na documentação oficial &lt;a href="https://caddyserver.com/docs/caddyfile/directives/basic_auth" rel="noopener noreferrer"&gt;basic_auth&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Esta configuração do Caddy Server oferece uma solução elegante para servir arquivos estáticos com autenticação, ao mesmo tempo que permite um endpoint de health check público. A flexibilidade e simplicidade do Caddy tornam-no uma excelente escolha para diversos cenários de deploy.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Using Caddy to Serve Static Files Behind a Load Balancer</title>
      <dc:creator>Edimar Cardoso</dc:creator>
      <pubDate>Thu, 03 Oct 2024 19:07:34 +0000</pubDate>
      <link>https://dev.to/quave/using-caddy-to-serve-static-files-behind-a-load-balancer-h2l</link>
      <guid>https://dev.to/quave/using-caddy-to-serve-static-files-behind-a-load-balancer-h2l</guid>
      <description>&lt;p&gt;&lt;a href="https://caddyserver.com/" rel="noopener noreferrer"&gt;Caddy server&lt;/a&gt; has proven to be a very promising project. Not only is it fast, but it also has many built-in features that greatly simplify configuration. One example is the automatic issuance of SSL certificates. Moreover, it's very easy to set up a service using Caddy.&lt;/p&gt;

&lt;p&gt;This week, I needed to create a service to publish static files. When trying to do this with Caddy's default configuration, I noticed it wouldn't be possible due to a slightly different scenario.&lt;/p&gt;

&lt;p&gt;In my scenario, the service would run behind a load balancer at &lt;a href="https://www.zcloud.ws" rel="noopener noreferrer"&gt;zCloud&lt;/a&gt; that's already responsible for generating certificates and handling TLS/SSL termination. The default configuration caused issues because it shouldn't generate an SSL certificate, but only serve files on the default HTTP port.&lt;/p&gt;

&lt;p&gt;Additionally, there was another particularity: I needed a health check endpoint with public access, while everything else needed to be blocked using standard "Basic Auth" authentication.&lt;/p&gt;

&lt;p&gt;Here's the file used to accomplish this in a simple way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  auto_https off
}
:8080 {
    route /_health* {
        respond 200
    }
    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's break down the configuration:&lt;/p&gt;

&lt;h2&gt;
  
  
  Disabling SSL
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;auto_https off
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This disables automatic certificate issuance and disables redirection to HTTPS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Port
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:8080 {
# ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This specifies the port that will be opened to receive connections.&lt;/p&gt;

&lt;h2&gt;
  
  
  Health check
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route /_health* {
        respond 200
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This route provides public access to the health check endpoint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Static files with authentication
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    route {
        basic_auth / {
            USER_NAME HASH_PASSWORD
        }
        root * /var/www/static
        file_server
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part of the configuration will receive all other requests, intercept them and force a login if the user is not logged in, and serve files from the &lt;code&gt;/var/www/static&lt;/code&gt; directory.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;USER_NAME&lt;/code&gt; value is the username that will be used for authentication.&lt;br&gt;
The &lt;code&gt;HASH_PASSWORD&lt;/code&gt; value is the hash generated by the Caddy CLI based on the defined password. To generate this value, simply follow the instructions in the official &lt;a href="https://caddyserver.com/docs/caddyfile/directives/basic_auth" rel="noopener noreferrer"&gt;basic_auth documentation&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;This Caddy Server configuration offers an elegant solution for serving static files with authentication while allowing a public health check endpoint. Caddy's flexibility and simplicity make it an excellent choice for various deployment scenarios.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AI, Cursor, Verbosity and Meteor without frontend frameworks</title>
      <dc:creator>Filipe Névola</dc:creator>
      <pubDate>Sun, 29 Sep 2024 13:40:36 +0000</pubDate>
      <link>https://dev.to/quave/ai-cursor-verbosity-and-meteor-without-frontend-frameworks-2jn8</link>
      <guid>https://dev.to/quave/ai-cursor-verbosity-and-meteor-without-frontend-frameworks-2jn8</guid>
      <description>&lt;h2&gt;
  
  
  Cursor: Changing the IDE Game
&lt;/h2&gt;

&lt;p&gt;Cursor provides three modes: Inline, Composer, and Chat. These modes offer unique ways to interact with AI during coding. However, they are not without their drawbacks.&lt;/p&gt;

&lt;p&gt;You might want to skip this section if you're a VSCode enthusiast who's never experienced JetBrains IDEs. As I say, ignorance is bliss, especially when it comes to food taste and IDEs.&lt;/p&gt;

&lt;p&gt;For those familiar with more robust IDEs, Cursor's limitations become apparent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Code navigation features are lacking (find usages, go-to reference, go-to implementation, etc.)&lt;/li&gt;
&lt;li&gt;Git integration is limited&lt;/li&gt;
&lt;li&gt;Search, find, and replace capabilities are weak&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I know it inherits all of VSCode's issues, but even when I configure 30+ keyboard shortcuts to do things I do naturally on JetBrains IDEs, the experience is still sufferable.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the possible best setup today?
&lt;/h2&gt;

&lt;p&gt;Use Cursor for AI-specific coding tasks and WebStorm (or other JetBrains IDEs) for everything else. The dream scenario would be JetBrains shamelessly copying all of Cursor's features.&lt;/p&gt;

&lt;p&gt;Cursor's AI capabilities shine because it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Utilizes Claude 3.5 Sonnet&lt;/li&gt;
&lt;li&gt;Provides three distinct interaction methods&lt;/li&gt;
&lt;li&gt;Allows Composer to modify and create multiple files simultaneously&lt;/li&gt;
&lt;li&gt;Offers separate diff and apply actions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Imagine having JetBrains' superior diff feature in Cursor – that would be a game-changer.&lt;/p&gt;

&lt;p&gt;To expand on "AI-specific coding tasks":&lt;/p&gt;

&lt;p&gt;I still prefer to code myself in many cases, though I expect these instances to decrease over time. I believe the sweet spot for AI now is handling simple tasks, especially in unfamiliar projects or when dealing with verbose tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verbosity: No Longer a Developer's Nemesis?
&lt;/h2&gt;

&lt;p&gt;Verbosity has long been a thorn in developers' sides, but AI-generated code is shifting this paradigm. This change could make verbose languages like Java and tools like TailwindCSS more palatable to developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meteor Frontend Without Frameworks: A Fresh Perspective
&lt;/h2&gt;

&lt;p&gt;Exploring Meteor's reactivity without client-side frameworks unveils how powerful the Meteor's reactive model is on its foundation. Here's a quick demo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Function to render the video list&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderVideoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoList&lt;/span&gt;&lt;span class="p"&gt;)&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;h2&amp;gt;YouTube Videos&amp;lt;/h2&amp;gt;
    &amp;lt;div&amp;gt;
      &amp;lt;input type="text" id="newVideoUrl" placeholder="Enter YouTube URL"&amp;gt;
      &amp;lt;button id="addVideoBtn"&amp;gt;Add Video&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
    &amp;lt;ul&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;videoList&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;video&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;li&amp;gt;
      &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
      &amp;lt;button onclick="removeVideo('&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;')"&amp;gt;Remove&amp;lt;/button&amp;gt;
    &amp;lt;/li&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&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;content&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;renderHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Add event listeners&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;addVideoBtn&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addVideo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;newVideoUrl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;keypress&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleNewVideoKeyPress&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Simple function to render HTML using JavaScript&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderHTML&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;content&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;element&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;elementId&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;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Element with id "&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;elementId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;" not found.`&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="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeVideo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videos.remove&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;videoId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// If successful, the UI should update automatically if you're using Tracker or ReactiveVar&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Error removing video:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="c1"&gt;// Optionally, show an error message to the user&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;Meteor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startup&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;Meteor.startup&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;Meteor&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;youtubeVideos&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Set up the reactive computation&lt;/span&gt;
  &lt;span class="nx"&gt;Tracker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;autorun&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;YoutubeVideosCollection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;renderVideoList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoList&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;As you can see, this HTML was generated by AI, so it was "coded in English," and I didn't have to type anything. Meteor's powerful foundation allowed me to create very simple code that is reactive and updates the UI in real-time without any UI framework.&lt;/p&gt;

&lt;p&gt;Why does this matter, and why I'm talking about it in this post? &lt;/p&gt;

&lt;p&gt;Because AI can now generate HTML based on natural language requests with ease and speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion: Embracing the New Era
&lt;/h2&gt;

&lt;p&gt;While we're not suggesting abandoning frameworks altogether, we're undoubtedly entering a new age where verbosity is becoming less of an issue for developers. It's time to consider embracing new patterns and languages.&lt;/p&gt;

&lt;p&gt;Good libraries and frameworks offer much more than just reducing verbosity. They optimize performance for common cases and propose innovative solutions to problems. However, exploring life without them is occasionally beneficial, as this can lead to fresh ideas and perspectives.&lt;/p&gt;

&lt;p&gt;As a bonus insight: TailwindCSS's verbose nature, once considered a drawback, is now less significant in the age of AI-assisted development. Some might call this lucky timing, but as the saying goes, "luck favors the prepared." TailwindCSS's success is a testament to its creators' hard work and foresight, who positioned themselves perfectly for this new era of development (with a good pinch of luck).&lt;/p&gt;

</description>
      <category>ai</category>
      <category>meteor</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
