<?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: Kevin Alemán</title>
    <description>The latest articles on DEV Community by Kevin Alemán (@kaleman15).</description>
    <link>https://dev.to/kaleman15</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F255126%2F6bdaeb84-6c1e-4e76-9d45-54c59eaac908.jpeg</url>
      <title>DEV Community: Kevin Alemán</title>
      <link>https://dev.to/kaleman15</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kaleman15"/>
    <language>en</language>
    <item>
      <title>Got featured :noice:</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Tue, 10 Mar 2026 16:45:59 +0000</pubDate>
      <link>https://dev.to/kaleman15/-i0</link>
      <guid>https://dev.to/kaleman15/-i0</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/devteam/top-7-featured-dev-posts-of-the-week-1889" class="crayons-story__hidden-navigation-link"&gt;Top 7 Featured DEV Posts of the Week&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;
          &lt;a class="crayons-logo crayons-logo--l" href="/devteam"&gt;
            &lt;img alt="The DEV Team logo" 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%2Forganization%2Fprofile_image%2F1%2Fd908a186-5651-4a5a-9f76-15200bc6801f.jpg" class="crayons-logo__image"&gt;
          &lt;/a&gt;

          &lt;a href="/jess" class="crayons-avatar  crayons-avatar--s absolute -right-2 -bottom-2 border-solid border-2 border-base-inverted  "&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%2Fuser%2Fprofile_image%2F264%2Fb75f6edf-df7b-406e-a56b-43facafb352c.jpg" alt="jess profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/jess" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Jess Lee
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Jess Lee
                &lt;a href="/++"&gt;&lt;img alt="Subscriber" class="subscription-icon" src="https://assets.dev.to/assets/subscription-icon-805dfa7ac7dd660f07ed8d654877270825b07a92a03841aa99a1093bd00431b2.png"&gt;&lt;/a&gt;
              
              &lt;div id="story-author-preview-content-3331226" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/jess" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F264%2Fb75f6edf-df7b-406e-a56b-43facafb352c.jpg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Jess Lee&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

            &lt;span&gt;
              &lt;span class="crayons-story__tertiary fw-normal"&gt; for &lt;/span&gt;&lt;a href="/devteam" class="crayons-story__secondary fw-medium"&gt;The DEV Team&lt;/a&gt;
            &lt;/span&gt;
          &lt;/div&gt;
          &lt;a href="https://dev.to/devteam/top-7-featured-dev-posts-of-the-week-1889" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 10&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/devteam/top-7-featured-dev-posts-of-the-week-1889" id="article-link-3331226"&gt;
          Top 7 Featured DEV Posts of the Week
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/discuss"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;discuss&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/top7"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;top7&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/devteam/top-7-featured-dev-posts-of-the-week-1889" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/fire-f60e7a582391810302117f987b22a8ef04a2fe0df7e3258a5f49332df1cec71e.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;47&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/devteam/top-7-featured-dev-posts-of-the-week-1889#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              18&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>top7</category>
      <category>discuss</category>
    </item>
    <item>
      <title>🙌🏽</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Sun, 08 Mar 2026 18:51:54 +0000</pubDate>
      <link>https://dev.to/kaleman15/-j59</link>
      <guid>https://dev.to/kaleman15/-j59</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482" class="crayons-story__hidden-navigation-link"&gt;In the AI Era, Code Is Cheap. Reputation Isn’t.&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/kaleman15" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F255126%2F6bdaeb84-6c1e-4e76-9d45-54c59eaac908.jpeg" alt="kaleman15 profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/kaleman15" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Kevin Alemán
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Kevin Alemán
                
              
              &lt;div id="story-author-preview-content-3309486" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/kaleman15" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F255126%2F6bdaeb84-6c1e-4e76-9d45-54c59eaac908.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Kevin Alemán&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Mar 4&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482" id="article-link-3309486"&gt;
          In the AI Era, Code Is Cheap. Reputation Isn’t.
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/opensource"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;opensource&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/programming"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;programming&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/software"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;software&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;39&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              15&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>In the AI Era, Code Is Cheap. Reputation Isn’t.</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Wed, 04 Mar 2026 21:01:29 +0000</pubDate>
      <link>https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482</link>
      <guid>https://dev.to/kaleman15/in-the-ai-era-code-is-cheap-reputation-isnt-3482</guid>
      <description>&lt;p&gt;In the era of AI it's easier than ever to be an Open Source contributor!&lt;/p&gt;

&lt;p&gt;But, at the same time, and quite paradoxically, it's harder than ever.&lt;/p&gt;

&lt;p&gt;Why? Because it's now &lt;em&gt;mechanically easier, but reputationally harder&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Previously, you competed against other incredibly smart volunteers, or maybe against employed developers that contributed their expertise and free time to open source, or against newcomers. Now, you compete against all of that and an army of AI agents generating pull requests, issues, refactors and vulnerability reports faster than what any human can do.&lt;/p&gt;

&lt;p&gt;It's pretty tempting. Using an LLM can reduce significantly the cognitive work you have to do to understand a codebase, and even to produce a valuable change request. Things that were previously "very hard" to do are now a few prompts away, and that lowered the entry barrier for contributions, which is great! But also created a massive increase in volume, which is flooding repositories.&lt;/p&gt;

&lt;p&gt;What's the problem? &lt;br&gt;
&lt;em&gt;Maintainer capacity did not increase&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Review cost did not decrease&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;These two truths have already led maintainer to resort to drastic measures, like what happened with &lt;code&gt;curl&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://daniel.haxx.se/blog/2025/07/14/death-by-a-thousand-slops/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdaniel.haxx.se%2Fblog%2Fwp-content%2Fuploads%2F2017%2F09%2Fbug-insect-1200x803.jpg" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://daniel.haxx.se/blog/2025/07/14/death-by-a-thousand-slops/" rel="noopener noreferrer" class="c-link"&gt;
            Death by a thousand slops | daniel.haxx.se
          &lt;/a&gt;
        &lt;/h2&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdaniel.haxx.se%2Fblog%2Fwp-content%2Fuploads%2F2024%2F07%2Fdaniel-greenbg-blackandwhite-413x413-1.jpg"&gt;
          daniel.haxx.se
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




&lt;p&gt;So, how do you become a valuable Open Source contributor in the era of infinite code generation? Here are a few tips learned from dealing with a large volume of contributions:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Think. Then think again
&lt;/h2&gt;

&lt;p&gt;AI can produce great code, but it can also produce the worst code ever seen by the humanity. This depends a lot on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;prompt&lt;/em&gt; that you use&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;quality&lt;/em&gt; of the LLM&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;context&lt;/em&gt; being shared&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And the most important one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your own coding abilities.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Why do you need to know how to code before using LLMs to contribute to open source?&lt;/p&gt;

&lt;p&gt;Well, &lt;em&gt;because an LLM cannot know everything.&lt;/em&gt;&lt;br&gt;
For example, we had a PR that attempted to change this:&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;        &lt;span class="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;Some error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;After&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;        &lt;span class="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;logger&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;Some error:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&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;You would say: that is a valid change, we want to use &lt;code&gt;logger&lt;/code&gt; instead of &lt;code&gt;console&lt;/code&gt;! What's the problem?&lt;/p&gt;

&lt;p&gt;The problem was that the LLM didn't know (and didn't check, cause it wasn't asked to) that we recently changed the typings on our &lt;code&gt;logger&lt;/code&gt; instances, rendering the change invalid.&lt;/p&gt;

&lt;p&gt;What a valid change would have looked like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;        &lt;span class="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;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;logger&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="na"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Some error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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;But, the LLM didn't know this, and it would have required quite some context for it to know. Good LLMs (Claude, Codex, etc) would have caught, but others would not, unless you asked them. How would you ask something that you didn't know? There's where your coding abilities enter into play.&lt;/p&gt;

&lt;p&gt;Here's the key point: &lt;em&gt;You cannot prompt what you don't know exists&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Someone that wants to be a contributor doesn't trust blindly on what the LLM produces. Knows how to use the tooling (typecheck, in this case) to catch those issues, reads the code to understand current conventions, caveats, etc.&lt;br&gt;
For this programmers, &lt;em&gt;LLMs become a powerful tool&lt;/em&gt;, cause it helps you with the mechanical task of coding and gives you enough time to think.&lt;/p&gt;

&lt;p&gt;This also protects your reputation. If maintainers see a pattern of low-signal PRs from you, they’ll eventually stop reviewing them. Or worse.&lt;/p&gt;

&lt;p&gt;LLMs make contribution faster, but they don’t replace your brain unless you let them.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Respect volume. Review is not free.
&lt;/h2&gt;

&lt;p&gt;You are happy. We're also happy that you want to contribute to open source. It's a noble endeavor and it's something that helps the repository to be safer &amp;amp; you to be better. It's a win-win!&lt;/p&gt;

&lt;p&gt;But, maintainers have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Limited capacity. &lt;/li&gt;
&lt;li&gt;Limited time. &lt;/li&gt;
&lt;li&gt;&lt;em&gt;Limited patience.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We understand, you are happy you created your new PR, and we understand the joy in you of doing that. We've all being there. You want, for sure, your PR to be merged so you can say with honor: "I contributed to X!"&lt;/p&gt;

&lt;p&gt;But, be reasonable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cost of opening a PR is now near zero.&lt;/li&gt;
&lt;li&gt;The cost of &lt;em&gt;reviewing&lt;/em&gt; it is not.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This asymmetry is the real tension of the AI era.&lt;/p&gt;

&lt;p&gt;If you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;comment “please review” repeatedly,&lt;/li&gt;
&lt;li&gt;ping maintainers aggressively,&lt;/li&gt;
&lt;li&gt;demand merging timelines,&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’re increasing the cognitive cost of your contribution. Even if your PR is valid.&lt;/p&gt;

&lt;p&gt;And here’s the harsh truth:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Maintainers are human.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If interacting with you feels expensive, they may subconsciously deprioritize your work. That means fewer reviews, fewer merges, fewer release notes mentions.&lt;/p&gt;

&lt;p&gt;This is specially important when you use LLMs or Agents as they are fast, way faster than humans, so you could end up doing 100 PRs that you think are valid, and maybe they are! But if the maintainers don't have the capacity to review them all, they just won't.&lt;/p&gt;

&lt;p&gt;That's the definition of a DoS (Denial of Service): you send more requests than what the server (maintainers) can process.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Quality that doesn't cut corners (pls don't sue me Wendy's)
&lt;/h2&gt;

&lt;p&gt;Maintainer capacity is low. We closed around 700 issues (stale, invalid, fixed) in the past month, but at the same time, we received 300 new issues. Some of those issues were valid, some of them were not. The problem is we don't know beforehand, &lt;em&gt;we have to check them all&lt;/em&gt;. You cannot trust an LLM to confirm if something is slop... cause the check may be slop too!&lt;/p&gt;

&lt;p&gt;Related link, &lt;code&gt;curl&lt;/code&gt; fighting against AI slop vulnerability reports:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://hackerone.com/reports/3516186" rel="noopener noreferrer"&gt;HackerOne Curl report&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you see, maintainers won't be happy if you waste their time, cause they have stuff to do. The link above, as well as this beautiful list&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;





&lt;p&gt;have something in common: they look legit, until they don't. However, for realising something is slop you have to check manually, and that is time/effort wasted.&lt;/p&gt;

&lt;p&gt;So you have to be human, respect the times maintainers have and respect their work. How you do that? &lt;em&gt;Helping the maintainers reduce the cost of reviewing your code&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;Before submitting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did you reproduce the issue yourself?&lt;/li&gt;
&lt;li&gt;Did you run CI locally?&lt;/li&gt;
&lt;li&gt;Did you include screenshots for UI changes?&lt;/li&gt;
&lt;li&gt;Did you provide benchmarks for performance claims?&lt;/li&gt;
&lt;li&gt;Did you include a PoC for security reports?&lt;/li&gt;
&lt;li&gt;Did you check if a feature request already exists?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you claim a performance improvement, &lt;em&gt;show numbers&lt;/em&gt;.&lt;br&gt;
If you fix a bug, &lt;em&gt;explain the root cause&lt;/em&gt;.&lt;br&gt;
If you propose a feature, &lt;em&gt;ask first&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;High quality contributions get reviewed faster.&lt;br&gt;
Low signal contributions accumulate.&lt;/p&gt;

&lt;p&gt;In the AI era, &lt;em&gt;reputation compounds&lt;/em&gt;. If maintainers can trust you, your PRs will be reviewed faster, and you will feel happier. Win-win!&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Bundle trivial changes.
&lt;/h2&gt;

&lt;p&gt;Typos happen. Smol mistakes happen. For example &lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;        &lt;span class="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;sub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;subsciption not found&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;p&gt;After&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;        &lt;span class="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;sub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&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;subscription not found&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;p&gt;Do you spot the bug? Yep, a missing &lt;code&gt;r&lt;/code&gt; on &lt;code&gt;subscription&lt;/code&gt;. The contribution is valid, yes, we want to write correct words in english.&lt;/p&gt;

&lt;p&gt;Does it deserve a PR? Not by itself, maybe if you combine a few more. Think if you find 10 of those misspellings. What's easier for maintainers? 1 PR with 10 changes or 10 PRs with 1 change? Hehe.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you were the maintainer, what would you prefer?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every PR has overhead: CI runs, notifications, review time, merge time, mental context switch, etc. Reduce the overhead by batching trivial work.&lt;/p&gt;

&lt;p&gt;AI can find an enormous list of fixes like this in a codebase, and if you use an agent like Claude or Clawd you can tell "Hey for each issue create a PR" and then become the most hated contributor on the project :(&lt;/p&gt;

&lt;p&gt;Don't do that. Please. Be mindful. &lt;em&gt;Be human&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  LLMs are fun, but human work is better.
&lt;/h2&gt;

&lt;p&gt;We're on the AI era, we cannot fight AIs anymore, they're here to stay. What we can fight is how we use them.&lt;/p&gt;

&lt;p&gt;For GSoC for example, we value human contributions. That's the point of the program: more humans converting to open source contributors!&lt;/p&gt;

&lt;p&gt;We need new generations of maintainers, of smart people that will use LLMs wisely. You can do amazing stuff with them if you use them properly, but nothing will replace your ability to do amazing stuff as a human. You have to remember that.&lt;/p&gt;

&lt;p&gt;It's easier to contribute, but harder to stand out from the crowd.&lt;br&gt;
Reputation matters.&lt;br&gt;
Trust is earned.&lt;br&gt;
You build it by doing good work.&lt;/p&gt;

&lt;p&gt;As a final note, this is pretty much personal experience. Some projects may have different rules. Some may like to receive 1000 PRs a day (?) idk.&lt;/p&gt;

&lt;p&gt;But, the main point is: AI is not human, but maintainers and contributors are. So remember that.&lt;/p&gt;

&lt;p&gt;Welcome to Open Source!&lt;/p&gt;

&lt;p&gt;If u want to read more:&lt;br&gt;


&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.jeffgeerling.com/blog/2026/ai-is-destroying-open-source/" rel="noopener noreferrer" class="c-link"&gt;
            AI is destroying Open Source, and it's not even good yet - Jeff Geerling
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Over the weekend Ars Technica retracted an article because the AI a writer used hallucinated quotes from an open source library maintainer.
The irony here is the maintainer in question, Scott Shambaugh, was harassed by someone's AI agent over not merging its AI slop code.
It's likely the bot was running through someone's local 'agentic AI' instance (likely using OpenClaw). The guy who built OpenClaw was just hired by OpenAI to "work on bringing agents to everyone." You'll have to forgive me if I'm not enthusastic about that.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
          jeffgeerling.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;





</description>
      <category>ai</category>
      <category>opensource</category>
      <category>programming</category>
      <category>software</category>
    </item>
    <item>
      <title>You're doing great, and you need to hear that :)</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Wed, 15 Sep 2021 16:09:03 +0000</pubDate>
      <link>https://dev.to/kaleman15/you-re-doing-great-and-you-need-to-hear-that-12ba</link>
      <guid>https://dev.to/kaleman15/you-re-doing-great-and-you-need-to-hear-that-12ba</guid>
      <description>&lt;p&gt;I'm not the kind of person that likes to be "thanked" for something. I like to do the things, and that's it. And later I realized it was not only me who felt this way. But, is receiving recognition something reserved for super special situations?&lt;/p&gt;

&lt;h2&gt;
  
  
  Everyone needs to be "praised" for doing a great work
&lt;/h2&gt;

&lt;p&gt;Being praised releases dopamine, that's why you feel happy when someone recognizes your work and says that it's good. Everyone likes that feeling of "doing something amazing" by receiving a simple "thanks for helping me". But, it doesn't stop there.&lt;/p&gt;

&lt;p&gt;Your motivation is also increased. It's important to note that everyone has a different set of motivators, so what may work for someone probably won't have the same effect for others. But there are some common motivators in a workplace, like the compensation. Receiving a considerable wage and a good compensation plan on your company may motivate you to work better. Same as having a clean workstation, or having a good team composed of capable people. Recognition is also a &lt;a href="https://www.thesuccessfactory.co.uk/blog/motivators-in-the-workplace-that-improve-employee-experience" rel="noopener noreferrer"&gt;key motivator&lt;/a&gt;, that means, surprisingly, that &lt;a href="https://www.trainingjournal.com/articles/feature/power-praise-and-recognition" rel="noopener noreferrer"&gt;people feel good&lt;/a&gt; when they're recognized :)&lt;/p&gt;

&lt;p&gt;However, as any other dopamine-releaser, the effect of recognition is transient, and, it may decrese the effect over time when it's done &lt;em&gt;too much&lt;/em&gt;. So, recognition should be given timely.&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%2Fk423mpsup0nb13ycv8l8.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk423mpsup0nb13ycv8l8.gif" alt="Syndrome's line" width="480" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Timely recognition
&lt;/h2&gt;

&lt;p&gt;So, how do we achieve a recognition scheme that's not sporadic but is not rare at the same time? Balance, as always, is the key.&lt;/p&gt;

&lt;p&gt;This is more an HR topic, probably at your company there's a lot of people trying to fix this same issue, because recognition is not as easy as it may look. Fairness plays a &lt;em&gt;really&lt;/em&gt; important role. What if another employee who does the same thing I do receives recognition every week, but I receive it every year? Then, recognition becomes a problem and a demotivator, so it's important to watch out how recognition will be conducted.&lt;/p&gt;

&lt;p&gt;But, let's return to the frequency. From this &lt;a href="https://www.businessinsider.com/how-often-do-employees-need-recognition-2012-8" rel="noopener noreferrer"&gt;BI Article&lt;/a&gt;, a weekly recognition scheme is good. Probably you don't need it to be too frequent. Think for example, on sprints. If your sprints are 2 weeks long, why not dedicating some time at the end to thank your colleagues for their work?&lt;/p&gt;

&lt;p&gt;The same article says that this type of recognition helps the employee to be engaged, and trying to get that recognition again, because people like that. Try to avoid recognizing your employees just with the normal performance review cycle, because that may be too late.&lt;/p&gt;

&lt;p&gt;Again, if your colleague did something &lt;em&gt;really&lt;/em&gt; good for you or the team, don't wait to say thanks. Recognition has a "shelf life", that means, it can expire if not used at the right moment!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthumbs.gfycat.com%2FEnchantingSafeArizonaalligatorlizard-max-1mb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fthumbs.gfycat.com%2FEnchantingSafeArizonaalligatorlizard-max-1mb.gif" alt="Apu getting expired item from the shelves" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, key points for timely recognition:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do it before it expires :)&lt;/li&gt;
&lt;li&gt;Don't wait to normal performance review cycles to recognize people&lt;/li&gt;
&lt;li&gt;Be fair&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to create a recognition culture?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Make it easy&lt;br&gt;
Your team should feel that it's easy to give recognition. If people find a lot of requirements or an extremely formal process for doing so, they won't do it.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be specific&lt;br&gt;
Saying "thanks" is good, but it's even better when you know &lt;em&gt;the reason&lt;/em&gt; for that. So saying "thanks for helping me with the last presentation" would be much more valuable than just saying thanks, because that means you're recognizing &lt;em&gt;a specific effort&lt;/em&gt;, and not just saying a canned response.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Connect the dots :)&lt;br&gt;
Relate the recognition with your company's values. This makes easier for your team to know how they're contributing to the company's objectives and culture, and that creates a strong culture :)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Make it for everyone&lt;br&gt;
Avoid having only managers giving recognition. Foster p2p recognition, since that'll create bounds between your team.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Key points taken from &lt;a href="https://www.greatgame.com/blog/creating-a-recognition-culture-best-practices" rel="noopener noreferrer"&gt;this article&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, as conclusion, thanks for what you're doing to improve the community :) and thanks for sharing your knowledge with the rest of the people using this platform.&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%2Furw26vjcobmnds2984jb.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Furw26vjcobmnds2984jb.jpg" alt="Simpsons' Monstromart: we love you" width="720" height="576"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, go there and recognize people for what they're doing :)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>mentalhealth</category>
      <category>beginners</category>
    </item>
    <item>
      <title>The beauty of Streams in Node</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Fri, 02 Oct 2020 21:06:17 +0000</pubDate>
      <link>https://dev.to/kaleman15/the-beauty-of-streams-in-node-28d</link>
      <guid>https://dev.to/kaleman15/the-beauty-of-streams-in-node-28d</guid>
      <description>&lt;p&gt;Probably you have heard of them, probably you don't. But they have been around for a while. We're talking about &lt;code&gt;streams&lt;/code&gt;, an interesting and often ignored functionality of Node.JS.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are streams?
&lt;/h2&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%2Fextension.unh.edu%2Fsites%2Fdefault%2Ffiles%2Fstyles%2F2x_blog_main%2Fpublic%2F2MountainStream_MCarp.jpg%3Fitok%3DDW1-tkGH%26timestamp%3D1516995141" 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%2Fextension.unh.edu%2Fsites%2Fdefault%2Ffiles%2Fstyles%2F2x_blog_main%2Fpublic%2F2MountainStream_MCarp.jpg%3Fitok%3DDW1-tkGH%26timestamp%3D1516995141" alt="a river, or stream?" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To make things easier, we will define a stream as a &lt;code&gt;sequence of data that flows freely&lt;/code&gt;. Think of streams like rivers. Rivers flow from one point to another in a constant way. The receiver doesn't know when the river will stop flowing but it's always there for receiving more water.&lt;/p&gt;

&lt;p&gt;In Node, streams are very similar. They are a constant sequence of data. They flow from one point (emitter) to another (receiver). &lt;/p&gt;

&lt;p&gt;The receiver can decide if it wants to receive data or not. Also it decides what to do with the data received. It can &lt;code&gt;ignore&lt;/code&gt; the data, &lt;code&gt;pipe&lt;/code&gt; it to another receiver, &lt;code&gt;parse&lt;/code&gt; it before receiving... &lt;/p&gt;

&lt;h2&gt;
  
  
  Why are streams useful?
&lt;/h2&gt;

&lt;p&gt;This is a really good question. If you have lived without streams your whole life, you will think that you don't need them. And, depending on your use case, this could be true.&lt;/p&gt;

&lt;p&gt;But the reality is that we need to use streams for some operations that otherwise will kill us. Let's put an example&lt;/p&gt;

&lt;h3&gt;
  
  
  Dealing with files
&lt;/h3&gt;

&lt;p&gt;Let's say we want to create a compressed copy of a file on our disk with a Node application. Usually, we will end up with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&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;zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gzip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.gz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;err&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;Done!!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Imports removed for brevity&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here, we're doing 4 things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're reading the &lt;em&gt;entire&lt;/em&gt; file and saving the data to a variable called &lt;code&gt;buffer&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;We're using the &lt;code&gt;zlib.gzip&lt;/code&gt; to compress the file into a &lt;code&gt;gzip&lt;/code&gt;. We're passing the buffer to the function call. This will return a new compressed buffer&lt;/li&gt;
&lt;li&gt;We write the compressed buffer to a new location&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;console.log&lt;/code&gt; indicating that the operation finished&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Well, this works. What's the problem with this approach? You may be wondering. Well, look again at the first 2 steps. In this approach, we're reading the &lt;em&gt;whole file&lt;/em&gt; before starting to process it. Additionally, we're storing the contents of that file in memory. This is not a problem if the file size is in MB. But, if the file size is in the order of GB? Hundreds of GB? Will your computer have all that RAM available to hold the file in it? Probably no. &lt;/p&gt;

&lt;p&gt;So, this approach, even when enough for more simple tasks, represents a problem when we're looking for performance and scalability, or simply we want to support larger files.&lt;/p&gt;

&lt;p&gt;The problem can be solved by using &lt;code&gt;streams&lt;/code&gt;. Let's see how:&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;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createReadStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;zlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createGzip&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createWriteStream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.gz&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="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;finish&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="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;wooh!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're doing some things different here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're creating a &lt;code&gt;read stream&lt;/code&gt;. This will return us &lt;code&gt;chunks&lt;/code&gt; of the file until it reaches the end.&lt;/li&gt;
&lt;li&gt;We're &lt;code&gt;piping&lt;/code&gt; the chunks to &lt;code&gt;zlib&lt;/code&gt; for compression. What's important here is that we are not waiting for the whole file to be read before starting the compression.&lt;/li&gt;
&lt;li&gt;We're creating a &lt;code&gt;write stream&lt;/code&gt; in which we're passing &lt;code&gt;chunks&lt;/code&gt; of data so Node can write them to the file.&lt;/li&gt;
&lt;li&gt;After all, we listen for the &lt;code&gt;finish&lt;/code&gt; event, which will be triggered when there's nothing more to do.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's a vast set of details &amp;amp; quirks &amp;amp; functions related to streams which will be covered in other blog posts.&lt;/p&gt;

&lt;p&gt;Hope you liked it!&lt;/p&gt;

</description>
      <category>node</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Building a new feature: talks preview</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Fri, 11 Sep 2020 06:59:14 +0000</pubDate>
      <link>https://dev.to/kaleman15/building-a-new-feature-talks-preview-4mp0</link>
      <guid>https://dev.to/kaleman15/building-a-new-feature-talks-preview-4mp0</guid>
      <description>&lt;p&gt;I'm a backend developer, so most of the time I'm doing backend things. I feel stressed most of the time when I need to do something at the front. &lt;/p&gt;

&lt;p&gt;Then, I started to build my own personal site. And it was great! It's a blog site located temporarily at &lt;a href="https://kaleman.netlify.app" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt; where you can contact me, read what I wrote, share my content with a suggested tweet, and &lt;em&gt;now&lt;/em&gt; you can see the talks I've given in my career. &lt;/p&gt;

&lt;p&gt;The talks are mostly about basic topics, one of them is in progress yet, but I wanted to showcase that I made them. And this was when the trouble begins&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%2F3vlfi3z52kggntvvn83l.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3vlfi3z52kggntvvn83l.gif" alt="prepare for the problems, and make it double" width="500" height="333"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  How?
&lt;/h3&gt;

&lt;p&gt;The first thing was to define what I wanted: I wanted to showcase my presentations. This was easy (in my mind), but, it wasn't easy in real life.&lt;/p&gt;

&lt;p&gt;To give you more context, I store all my presentations in Google Drive, so I can work on them wherever I go. This makes things easier for working on them, but a little bit hard if you want to show them offline.&lt;/p&gt;

&lt;p&gt;Why? Because first, you have to download the file. This is a no-brain task with a UI, just right-click and download. GG. The task gets more complex when you want &lt;em&gt;your code&lt;/em&gt; to perform that process. So I walked a lonely road, the only one that I have ever known: looking for someone who tried the same and succeeds. &lt;/p&gt;

&lt;p&gt;I found multiple &lt;a href="https://www.gatsbyjs.com/plugins" rel="noopener noreferrer"&gt;Gatsby plugins&lt;/a&gt; by just typing &lt;code&gt;drive&lt;/code&gt; or &lt;code&gt;google drive&lt;/code&gt; into the search box. I tried every single one, with the hope of finding what I wanted. Here's a list of the highlighted ones if you are trying to do something similar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/cedricdelpoux/gatsby-source-google-docs" rel="noopener noreferrer"&gt;gatsby-source-google-docs&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem: the use case I had in mind was very different from what the library did&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/fabe/gatsby-plugin-drive" rel="noopener noreferrer"&gt;gatsby-plugin-drive&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem: it only allowed me to download DOC files (not presentations, which was what I wanted)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/jpalmieri/gatsby-source-drive" rel="noopener noreferrer"&gt;gatsby-source-drive&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem: it didn't work. Maybe I misconfigured something or so.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/maxsteenbergen/gatsby-plugin-drive" rel="noopener noreferrer"&gt;gatsby-plugin-docs-sheets&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The problem: it only allowed me to download Sheets. I'm pretty sure this was based on &lt;code&gt;gatsby-plugin-drive&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, since no plugin was able to fulfill my needs, I had to make a difficult decision:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Give up on my feature&lt;/li&gt;
&lt;li&gt;Create my own plugin&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I took the second, and this plugin was the result: &lt;a href="https://github.com/KevLehman/gatsby-plugin-googledrive" rel="noopener noreferrer"&gt;Gatsby-plugin-googledrive&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The entire plugin was based on a single idea: given a Google Drive's &lt;code&gt;folderID&lt;/code&gt;, download all files from the folder, and traverse the subfolders recursively to create the same structure on your selected &lt;code&gt;destination&lt;/code&gt;. And download the files for each folder.&lt;/p&gt;

&lt;p&gt;The technical details can be found at the GitHub of the project and, it's open-source, so if you want to build something around it feel free to do so.&lt;/p&gt;

&lt;h3&gt;
  
  
  How? Part 2
&lt;/h3&gt;

&lt;p&gt;After creating my plugin, I "plugged" it and the download started. I finally had the files on my local, and this was one step closer to my goal.&lt;/p&gt;

&lt;p&gt;I had the PDFs on my local, now, I wanted to do 3 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get the URL from the PDF in my filesystem (and application!)&lt;/li&gt;
&lt;li&gt;Get the first page of the PDF (the "cover page")&lt;/li&gt;
&lt;li&gt;Convert that page to an image, and show it on a nice grid&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Soo, I needed 3 things: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One way to get the PDF from my filesystem&lt;/li&gt;
&lt;li&gt;One way to read the PDF&lt;/li&gt;
&lt;li&gt;One way to convert the read PDF into images&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the first item, since I'm using Gatsby, I used the &lt;a href="https://github.com/gatsbyjs/gatsby/tree/master/packages/gatsby-source-filesystem" rel="noopener noreferrer"&gt;gatsby-source-filesystem&lt;/a&gt; to read my folder and get the actual asset URL pointing to my file. It was really easy, just added this to my &lt;code&gt;gatsby-config.js&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nl"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gatsby-source-filesystem&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nl"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;talks&lt;/span&gt;&lt;span class="dl"&gt;"&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;${__dirname}/src/talks/&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that's it! Then, to query my filesystem and get the &lt;code&gt;nodes&lt;/code&gt; with data, some &lt;code&gt;graphql&lt;/code&gt; was needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight graphql"&gt;&lt;code&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="k"&gt;query&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="n"&gt;allFile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filter&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="n"&gt;sourceInstanceName&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="n"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"talks"&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="n"&gt;edges&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="n"&gt;node&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="n"&gt;name&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="n"&gt;publicURL&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;  
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That query will do 2 things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get all the files under the &lt;code&gt;path&lt;/code&gt; of the &lt;code&gt;instance name&lt;/code&gt; called &lt;code&gt;talks&lt;/code&gt; (which is the &lt;code&gt;name&lt;/code&gt; property you pass to the plugin definition)&lt;/li&gt;
&lt;li&gt;For each &lt;code&gt;node&lt;/code&gt; aka &lt;code&gt;file&lt;/code&gt;, get its &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;publicURL&lt;/code&gt; (which is the URL that the asset will have after the building)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the second item, I had to do some research. After some looking, I finally found &lt;a href="https://github.com/mozilla/pdfjs-dist" rel="noopener noreferrer"&gt;PDF.JS&lt;/a&gt; which is like the standard for managing PDFs in JavaScript. It was written by Mozilla and has a ton of useful functions. I just used the basic API to get my feature working in a few LoC.&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;PDFJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDocument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;talk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicURL&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pdf&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPage&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="nf"&gt;then&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;// some code goes 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;The introduction to the library in its GitHub page was simple, maybe too small for my taste, but it worked. With this, the only thing left was to find a way to convert the PDF page's data into an actual image.&lt;/p&gt;

&lt;p&gt;I found a pretty nice approach to accomplish that. It worked like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Read the PDF page&lt;/li&gt;
&lt;li&gt;Scale the page's width and height (by using the viewport of the page)&lt;/li&gt;
&lt;li&gt;Create a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;li&gt;Set the canvas context to &lt;code&gt;2d&lt;/code&gt; (since we'll draw an image)&lt;/li&gt;
&lt;li&gt;Set the scaled page as the new data context for the &lt;code&gt;canvas&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full code was linked in this &lt;a href="https://stackoverflow.com/a/12921304" rel="noopener noreferrer"&gt;StackOverflow's answer&lt;/a&gt; which even had an example of how it worked. Nice.&lt;/p&gt;

&lt;p&gt;With all of that in place, the final result: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fs0f8n6jf00jerp5mu57f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fs0f8n6jf00jerp5mu57f.png" alt="My site, with the talks page populated" width="800" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope you liked the article, feel free to also visit this post on my &lt;a href="https://kaleman.netlify.app/" rel="noopener noreferrer"&gt;own website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>gatsby</category>
      <category>javascript</category>
      <category>showdev</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Algos in real life: binary search</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Fri, 04 Sep 2020 20:14:12 +0000</pubDate>
      <link>https://dev.to/kaleman15/algos-in-real-life-binary-search-18jf</link>
      <guid>https://dev.to/kaleman15/algos-in-real-life-binary-search-18jf</guid>
      <description>&lt;p&gt;You probably know about algorithms. You probably don't. So here's a basic definition: an algorithm is a chain of steps for moving from A to B.&lt;/p&gt;

&lt;p&gt;If you want to make a sandwich, you probably have an algorithm for that. Mine is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Get the ingredients&lt;/li&gt;
&lt;li&gt;Separate bread and put it on a plate&lt;/li&gt;
&lt;li&gt;Put some dressing on both halves&lt;/li&gt;
&lt;li&gt;Put lettuce, tomatoes, pickles (if any) on one half&lt;/li&gt;
&lt;li&gt;Put ham, cheese, ham again on one half&lt;/li&gt;
&lt;li&gt;Add extra things (this is, probably whatever I can find that fits my sandwich)&lt;/li&gt;
&lt;li&gt;Put the empty half over the other ingredients&lt;/li&gt;
&lt;li&gt;Eat&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Whatever your process is, the point is you use an algorithm for that.&lt;/p&gt;

&lt;p&gt;Algos are really useful, in programming and real life. Here, I'll show you an example of how I used one of those algos to make my life (and work) easier.&lt;/p&gt;

&lt;h3&gt;
  
  
  The task
&lt;/h3&gt;

&lt;p&gt;Suppose that you were assigned to solve an issue at work. Depending on the kind of issue this may be easy peasy lemon squeezy or you can end stressed depressed lemon zest. &lt;br&gt;
The issue in this example is a simple one: a function was working at some time in the past, but now it's not. Easy.&lt;/p&gt;

&lt;p&gt;You fix the function, upload your changes, and create a merge request. Everything fine at this point.&lt;/p&gt;

&lt;p&gt;Then, someone from above asks the hard question: "Hey, this worked in the past, could you tell me &lt;em&gt;when&lt;/em&gt; it stopped working?". If like mine, your project has a ton of commits, then you'll have fun, more if your commit history looks like this:&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%2Flqskzvhh87ix4p3mmhru.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%2Flqskzvhh87ix4p3mmhru.png" alt="xkcd commit history" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;

&lt;p&gt;You have 3 options here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Cry in a corner&lt;/li&gt;
&lt;li&gt;Traverse &lt;em&gt;all&lt;/em&gt; your commits until you find one where it was working&lt;/li&gt;
&lt;li&gt;Do something good with the knowledge you have&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok, after doing the first we can start thinking on a real solution. And here's where algorithms come handy. Let's remember one of them!&lt;/p&gt;

&lt;h4&gt;
  
  
  The binary search algo, or the principle of divide and conquer.
&lt;/h4&gt;

&lt;p&gt;You may remember that algorithm from a class, a course, some reading, or any other place. In case you haven't, I'll leave a brief explanation here: &lt;/p&gt;

&lt;p&gt;The main idea of this algorithm is to, given a sorted list and a value to find as inputs, traverse the list to find the value you're looking for.&lt;/p&gt;

&lt;p&gt;The main point of difference between this and other search algos is &lt;em&gt;how&lt;/em&gt; the list is traversed. &lt;/p&gt;

&lt;p&gt;In traditional searching, you go from the start until the end (in the worst case) to find what you're looking for. If you reach the end and you haven't found the value, then it's not there. Something like this:&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%2F7iuxzyifcqe9ge9xt4d9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iuxzyifcqe9ge9xt4d9.gif" alt="Linearsearch" width="438" height="180"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a binary search, however, you "divide" the list in halves, then compare the middle value with what you're looking for. If the value is greater, then you repeat the process in the left half, otherwise you use the right half. You repeat this until you cannot divide your input list anymore. Something like this:&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%2Fdaic2okdulcwwggm1wdz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdaic2okdulcwwggm1wdz.gif" alt="Binary search" width="773" height="406"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To use a binary search, you just have to follow some rules:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your list must be sorted&lt;/li&gt;
&lt;li&gt;Your list must be sorted&lt;/li&gt;
&lt;li&gt;Your list &lt;em&gt;must&lt;/em&gt; be sorted&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Ok. You got this, the list must be sorted. Let's continue with the solution!&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution (part 2)
&lt;/h3&gt;

&lt;p&gt;Since our commit history is sorted (by the date of the commit) we can perform a binary search on the commit history!&lt;/p&gt;

&lt;p&gt;I selected some old commit (like 20 days before) as my start point and my current commit as the last item. &lt;br&gt;
I, then, selected the middle one (this was made visually) and &lt;code&gt;checkout&lt;/code&gt;ed to that commit.&lt;/p&gt;

&lt;p&gt;Was functionality working? Yes? Select a more recent one! Else, use an older one.&lt;/p&gt;

&lt;p&gt;You know how the story ends. I was able to verify the whole commit history in like 20 minutes, when it may have taken 2 hours due to the number of commits in the pool.&lt;/p&gt;

&lt;p&gt;Moral: if you know how to do something fast, do it! And, algos are also useful in real life!&lt;/p&gt;

&lt;p&gt;Hope you liked this history, and let me know if it helped you! Thanks for reading 🎉&lt;/p&gt;

</description>
      <category>git</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>development</category>
    </item>
    <item>
      <title>Developing JS/TS with VIM</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Thu, 18 Jun 2020 06:48:58 +0000</pubDate>
      <link>https://dev.to/kaleman15/developing-js-ts-with-vim-49ke</link>
      <guid>https://dev.to/kaleman15/developing-js-ts-with-vim-49ke</guid>
      <description>&lt;p&gt;In the beginning, there were editors. And VIM was an editor. And VIM was &lt;em&gt;the&lt;/em&gt; editor.&lt;/p&gt;

&lt;p&gt;I started to code 3 years ago. When I started to code, I remember having one coworker that was using Emacs. He just stared at its screen without using the mouse, and all he was doing was using the keyboard to do what he needed. He tried to convince me to use Emacs as well, but something didn't feel so good when using it.&lt;/p&gt;

&lt;p&gt;I, then, started to use VSCode. The good VSCode. I don't have anything to say against him. VSCode is an excellent editor. The only thing that stressed me out was its insane memory usage! After all, it's built in the top of Chromium, so it will use the RAM accordingly. VSCode is a perfect editor for almost anything you want to do. Do you want to write Java? There's a plugin. Do you want to test your API? There's a plugin. Do you want to manage your database? There. Is. A. Plugin. It's not an IDE because they haven't given the title to it!&lt;/p&gt;

&lt;p&gt;Even when that rich ecosystem is something beautiful by itself, it can led to some issues. The memory usage may go insane as you install more extensions. And disabling them based on the workspace (which you have to configure manually) instead of filetype stressed me more. &lt;/p&gt;

&lt;p&gt;So, there was a time, when I was working in a project that used a lot of docker containers, they used a lot of RAM by themselves, and I was using VSCode too. The RAM cake was starting to finish. Also, I had Chrome and another applications that I use to do my work (like Slack, which is also on top of Chromium) trying to get a piece of cake too... My computer didn't like that, so it crashed. This happened consistently for one month. I tried to change browser, to use Slack in my phone, to do some kind of black magic to get the most of my PC, even I tried to &lt;a href="https://downloadmoreram.com/" rel="noopener noreferrer"&gt;download&lt;/a&gt; more RAM (I really expected this one to work... :( ). But nothing happened. &lt;/p&gt;

&lt;p&gt;Then, the illumination came to me.&lt;/p&gt;

&lt;p&gt;I took a course, and one of the instructors was using VIM, but it was like an IDE. It was perfect. He moved so quickly between tabs (buffers) and commands were intuitive. The color scheme was great, the speed was really impressive. He had autocompletion and listing... everything was perfect. &lt;/p&gt;

&lt;p&gt;Motivated by this, I went to my laptop and installed VIM. But... &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%2Fi%2Fw4ehqeleysrz2ubtxo2j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fw4ehqeleysrz2ubtxo2j.png" alt="first vim opening" width="800" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IT WAS NOT LIKE THE VIDEO. WHY? I didn't have the autocomplete, neither the status bar nor the file explorer... it was just an empty console. Like Emacs was. And also I wasn't able to get out of it.&lt;/p&gt;

&lt;p&gt;I realized that Vim was not pretty out-of-the-box. You have to work on it before using it to write code. So I spent a few days reading VIM tutorials and contents. I looked for online resources, and found these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.openvim.com/" rel="noopener noreferrer"&gt;Try VIM online&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vim-adventures.com/" rel="noopener noreferrer"&gt;Learn VIM while playing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://learnvimscriptthehardway.stevelosh.com/" rel="noopener noreferrer"&gt;Learn VimScript the hard way&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After finishing 2 of those (obviously the first 2...) I was ready to start to use VIM.&lt;/p&gt;
&lt;h2&gt;
  
  
  The shorcuts
&lt;/h2&gt;

&lt;p&gt;This is a list of the basic shortcuts I learnt before using VIM. These ones allowed me to at least know how to use it. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To exit vim:
&lt;code&gt;:q&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To enter in edit mode:
&lt;code&gt;i&lt;/code&gt; (yeah, just type &lt;code&gt;i&lt;/code&gt; and you will get into edit mode)&lt;/li&gt;
&lt;li&gt;To exit edit mode (and all other modes):
&lt;code&gt;esc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To "save a file":
&lt;code&gt;:w&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To close without "saving":
&lt;code&gt;:q!&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To split "tabs" vertically:
&lt;code&gt;Ctrl + wv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To split "tabs" horizontally:
&lt;code&gt;Ctrl + ws&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;To change between "tabs":
&lt;code&gt;Ctrl + ww&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The plugins
&lt;/h2&gt;

&lt;p&gt;First of all, like every person looking for knowledge, I typed &lt;code&gt;How to add plugins to vim&lt;/code&gt; in Google. I followed some links and one of them took me to &lt;a href="https://github.com/tpope/vim-pathogen" rel="noopener noreferrer"&gt;Pathogen&lt;/a&gt;, which is a package manager that uses Git (just Git) to get the plugins. I also read about &lt;a href="https://github.com/junegunn/vim-plug" rel="noopener noreferrer"&gt;VimPlug&lt;/a&gt;, but Pathogen was my choice.&lt;/p&gt;

&lt;p&gt;Why? Well, I liked the way it should be enabled:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;execute pathogen#infect()
syntax on
filetype plugin indent on
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you see? Pathogen "infects" VIM. Isn't it nice? Well, I probably lol'ed so hard when reading that, more than I should. That convinced me to use it instead of VimPlug.&lt;/p&gt;

&lt;p&gt;Later, I discovered VIM 8 had native plugin support... &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%2Fi.kym-cdn.com%2Fentries%2Ficons%2Foriginal%2F000%2F006%2F306%2FFlipTable.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.kym-cdn.com%2Fentries%2Ficons%2Foriginal%2F000%2F006%2F306%2FFlipTable.jpg" alt="flipping the table" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The plugin list
&lt;/h3&gt;

&lt;p&gt;Now that I had a plugin manager, I needed plugins. Where did I find them? I used &lt;a href="https://vimawesome.com/" rel="noopener noreferrer"&gt;Vim Awesome&lt;/a&gt; to get a list of most used plugins and filter the ones that were related to JS/TS/Go. Then, I installed them. Here's the list of the modules I have downloaded for my current development:&lt;/p&gt;

&lt;p&gt;(Images taken from Vimawesome)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vimawesome.com/plugin/nerdtree-red" rel="noopener noreferrer"&gt;The NERDTree&lt;/a&gt; (to interact with the filesystem)&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%2Ftjlt55ffojt6rqolv68v.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%2Ftjlt55ffojt6rqolv68v.png" alt="nerdtree look and feel" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vimawesome.com/plugin/vim-airline-superman" rel="noopener noreferrer"&gt;Vim Airline&lt;/a&gt; (to get nice look and feel)&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%2Fs04cczhszzoqplev6qfa.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs04cczhszzoqplev6qfa.gif" alt="vim airline look and feel" width="641" height="72"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vimawesome.com/plugin/fugitive-vim" rel="noopener noreferrer"&gt;Vim Fugitive&lt;/a&gt; (this plugin is so awesome that it should be illegal)&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%2Fi%2Ftropsiz4uvt87h9uz7bu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ftropsiz4uvt87h9uz7bu.png" alt="example git log ran from fugitive.vim" width="800" height="406"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vimawesome.com/plugin/vim-polyglot" rel="noopener noreferrer"&gt;Vim Polyglot&lt;/a&gt; (for really good syntax highlight) &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%2Fi%2Fqzirhss8oiiea53zi9oj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqzirhss8oiiea53zi9oj.png" alt="Typescript syntax highlight with Polyglot" width="800" height="247"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vimawesome.com/plugin/ale" rel="noopener noreferrer"&gt;ALE&lt;/a&gt; (An asynchronous linting engine. This is very useful to configure &lt;code&gt;ESLint&lt;/code&gt; and &lt;code&gt;Prettier&lt;/code&gt; in Vim)&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%2Ftsrzkb974btf6fm77buh.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftsrzkb974btf6fm77buh.gif" alt="example ale linting js code" width="600" height="330"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://vimawesome.com/plugin/coc-nvim" rel="noopener noreferrer"&gt;COC.vim&lt;/a&gt; (the ultimate code completion plugin)&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%2Fs1o4waospzry2hd0xm6h.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs1o4waospzry2hd0xm6h.gif" alt="example coc.vim suggesting code" width="1504" height="784"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These aren't all the plugins I use. I just wrote there the most important ones. The ones without its help I wouldn't be able to write code in VIM. &lt;/p&gt;

&lt;p&gt;The final product, using the plugins and one theme that I found (it's called &lt;code&gt;onehalfdark&lt;/code&gt;) was this: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmnpha41bz41n3sjhxrlq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmnpha41bz41n3sjhxrlq.png" alt="my vim after the time investment on it" width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope you liked my intro to the Vim world, and maybe this motivates you to start getting into Vim! &lt;/p&gt;

</description>
      <category>vim</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fuzzy Searching with PostgreSQL</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Mon, 11 May 2020 17:12:11 +0000</pubDate>
      <link>https://dev.to/kaleman15/fuzzy-searching-with-postgresql-97o</link>
      <guid>https://dev.to/kaleman15/fuzzy-searching-with-postgresql-97o</guid>
      <description>&lt;h1&gt;
  
  
  What's fuzzy search?
&lt;/h1&gt;

&lt;p&gt;A fuzzy search is a type of search where the items are returned even when they are not an exact match. A fuzzy search is performed by a &lt;code&gt;fuzzy&lt;/code&gt; algorithm that evaluates the likeliness between the search query and the values, even when the search query is misspelled or the order of the words changed.&lt;/p&gt;

&lt;p&gt;For example, let's assume that we're using Google to find information about &lt;code&gt;washin machines&lt;/code&gt;. As you can see, the word &lt;code&gt;washing&lt;/code&gt; is misspelled. But even when Google knows that it'll return results that are somewhat similar to what you typed. Sometimes it even tries to correct you!&lt;/p&gt;

&lt;p&gt;Well, that's a &lt;code&gt;fuzzy search&lt;/code&gt;. Returning elements that are just &lt;code&gt;similar&lt;/code&gt; one to another. &lt;/p&gt;

&lt;h1&gt;
  
  
  How's searching occasionally implemented?
&lt;/h1&gt;

&lt;p&gt;Let's take a typical requirement: allow a user to search other users by its name. To accomplish that, you may end up building a query like this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;full_name&lt;/span&gt; &lt;span class="k"&gt;ilike&lt;/span&gt; &lt;span class="s1"&gt;'%query_param%'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assuming you have a user named &lt;code&gt;Jonh Doe&lt;/code&gt; in your database, looking for &lt;code&gt;Jonh&lt;/code&gt;, &lt;code&gt;JonH&lt;/code&gt;, &lt;code&gt;jOnh&lt;/code&gt;, &lt;code&gt;jonh doe&lt;/code&gt;, &lt;code&gt;Jonh Doe&lt;/code&gt; and so on will return the value you're looking for. But, what if the user types &lt;code&gt;Doe Jonh&lt;/code&gt;? &lt;/p&gt;

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

&lt;p&gt;Well, for sure your app won't crash with that input. But I'm pretty sure it won't return anything! Why? Because the order of the words is different than what's stored on the database, and &lt;code&gt;ilike&lt;/code&gt; won't do that rearrange of the words to match. One way to overcome this is to query also for the words in a different order, but that's not elegant.&lt;/p&gt;

&lt;p&gt;Apart from that, what happens if now the user (yes, always the user, ugh) types &lt;code&gt;john doe&lt;/code&gt;? There are some ways to handle this with &lt;code&gt;ilikes&lt;/code&gt; but, why doing that amount of work when you can let PostgreSQL do that for you?&lt;/p&gt;

&lt;p&gt;Let's introduce &lt;code&gt;pg_trgm&lt;/code&gt;. A PostgreSQL extension based on something called a &lt;code&gt;trigram&lt;/code&gt; that will be very useful for this! But, first of all, what's a &lt;code&gt;trigram&lt;/code&gt;?&lt;/p&gt;

&lt;h1&gt;
  
  
  PostgreSQL trigrams. What are they?
&lt;/h1&gt;

&lt;p&gt;The basic definition of trigram is: &lt;em&gt;"A group of 3 consecutive written units, such as words, letters, etc"&lt;/em&gt;. The &lt;code&gt;pg_trgm&lt;/code&gt; extension allows us to divide a word in such.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does a trigram look like?
&lt;/h2&gt;

&lt;p&gt;To see how a trigram look like, let's enable the PostgreSQL extension first!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;pg_trgm&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, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;show_trgm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'abcdefghijk'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                        &lt;span class="n"&gt;show_trgm&lt;/span&gt;                        
&lt;span class="c1"&gt;---------------------------------------------------------&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"  a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;" ab"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;abc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;bcd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;cde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;def&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;efg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;fgh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ghi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hij&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ijk&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"jk "&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&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 see, it'll divide the word into groups of 3 characters. Why there are spaces in the first and last trigrams? Well, that's because PostgreSQL assumes that every word to be converted into a trigram has 2 spaces before and one space after. Why? This allows PostgreSQL to create trigrams for words with less than 3 characters (&lt;code&gt;we&lt;/code&gt;, for example). Also, this makes the similarity to take into account the first and last characters, which otherwise would have less weight when the trigrams are generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use them to find similar words?
&lt;/h2&gt;

&lt;p&gt;If we select the trigrams for 2 words, we should be able to find how similar those strings are. How? By counting the number of trigrams they share!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;show_trgm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Friend'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
              &lt;span class="n"&gt;show_trgm&lt;/span&gt;              
&lt;span class="c1"&gt;-------------------------------------&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"  f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;" fr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;fri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ien&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"nd "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rie&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;show_trgm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Friendship'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                      &lt;span class="n"&gt;show_trgm&lt;/span&gt;                      
&lt;span class="c1"&gt;-----------------------------------------------------&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"  f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;" fr"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;dsh&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;fri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;hip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;ien&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"ip "&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;nds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;rie&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;shi&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How we find those similar trigrams between the 2 sets? Correct, an intersection!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;                                                 
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;show_trgm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'friends'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;
&lt;span class="k"&gt;intersect&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;show_trgm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'friendship'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;a&lt;/span&gt;  
&lt;span class="c1"&gt;-----&lt;/span&gt;
 &lt;span class="n"&gt;rie&lt;/span&gt;
 &lt;span class="n"&gt;fri&lt;/span&gt;
  &lt;span class="n"&gt;fr&lt;/span&gt;
   &lt;span class="n"&gt;f&lt;/span&gt;
 &lt;span class="k"&gt;end&lt;/span&gt;
 &lt;span class="n"&gt;ien&lt;/span&gt;
 &lt;span class="n"&gt;nds&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="k"&gt;rows&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, there are 7 trigrams common between the 2 sets. Now, how do we know how similar they are? We can do that by calculating a similarity coefficient between the 2 words. How?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;word_similarity = common_trigrams / first_word_total_trigrams
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why we use the first word? Well, because we're comparing the first against the second word. We don't care about the excedent trigrams from the second word because we know they won't correlate to the first one.&lt;/p&gt;

&lt;p&gt;Now, let's apply the formula. We have &lt;code&gt;7.00&lt;/code&gt; common trigrams, and we have &lt;code&gt;8.00&lt;/code&gt; total trigrams in the first word. Let's use PostgreSQL to determine the coefficient:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="k"&gt;column&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;        
&lt;span class="c1"&gt;------------------------&lt;/span&gt;
 &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;87500000000000000000&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Here, we use decimals because otherwise, PostgreSQL will ignore the decimal part of the operation, thus returning us 0.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Now, we know that our words have a similarity of &lt;code&gt;0.875&lt;/code&gt;. That means they are very similar! But, having to do all of this manually may take time... there should be a better way. &lt;/p&gt;

&lt;p&gt;Well, there's a better way! Alongside the trigram extension, we enabled a set of functions that came in the same packages. For example, the whole process we've done for calculating the similarity is abstracted away into a single function: &lt;code&gt;word_similarity&lt;/code&gt;. By using the function, we can get all of this process into a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;word_similarity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'friends'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'friendship'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="n"&gt;word_similarity&lt;/span&gt; 
&lt;span class="c1"&gt;-----------------&lt;/span&gt;
           &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;875&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And even better, there's a set of &lt;a href="https://www.postgresql.org/docs/9.6/pgtrgm.html" rel="noopener noreferrer"&gt;useful operators&lt;/a&gt; that are built into the same package we're using! For example, we may let PostgreSQL decide if two words are similar or not by using the &lt;code&gt;&amp;lt;%&lt;/code&gt; operator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="s1"&gt;'friends'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="s1"&gt;'friendship'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="k"&gt;column&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; 
&lt;span class="c1"&gt;----------&lt;/span&gt;
 &lt;span class="n"&gt;t&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That operator returns if 2 words are similar or not by using a &lt;code&gt;word_similarity_threshold&lt;/code&gt;, which we can query too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;show_limit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="n"&gt;show_limit&lt;/span&gt; 
&lt;span class="c1"&gt;------------&lt;/span&gt;
        &lt;span class="mi"&gt;0&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="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Note that &lt;code&gt;show_limit()&lt;/code&gt; function is deprecated, but still works.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That limit is, the minimum coefficient a pair of words can have before marking them as different. You can modify that threshold using the &lt;code&gt;set_limit()&lt;/code&gt; function and provide new value, but for most use cases, the default one should work!&lt;/p&gt;

&lt;p&gt;Using trigrams, we can also query for the "distance" between 2 words, which is calculated by taking the &lt;code&gt;similarity&lt;/code&gt; of the words from &lt;code&gt;1&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;distance = 1 - similarity(word_1, word_2);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the &lt;code&gt;&amp;lt;-&amp;gt;&lt;/code&gt; operator. The lower the number the more similar (or less distance) they have.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="s1"&gt;'friends'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'friendship'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="k"&gt;column&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; 
&lt;span class="c1"&gt;----------&lt;/span&gt;
 &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;416667&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use this operator to build something like Google's &lt;code&gt;did you mean&lt;/code&gt; functionality, which is probably done with n-grams.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building a &lt;code&gt;Did you mean&lt;/code&gt; engine
&lt;/h3&gt;

&lt;p&gt;First, let's create a table and populate it with data. For this example, I'll be using the departments of my country, El Salvador.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="n"&gt;t_departments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, some data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="k"&gt;into&lt;/span&gt; &lt;span class="n"&gt;t_departments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="k"&gt;values&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'San Salvador'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Chalatenango'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Ahuachapan'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cabanas'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Sonsonate'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'La Libertad'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Santa Ana'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'La Union'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Cuscatlan'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'San Vicente'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'La Paz'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Usulutan'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Morazan'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'San Miguel'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;t_departments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="n"&gt;name&lt;/span&gt;     
&lt;span class="c1"&gt;--------------&lt;/span&gt;
 &lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="n"&gt;Salvador&lt;/span&gt;
 &lt;span class="n"&gt;Chalatenango&lt;/span&gt;
 &lt;span class="n"&gt;Ahuachapan&lt;/span&gt;
 &lt;span class="n"&gt;Cabanas&lt;/span&gt;
 &lt;span class="n"&gt;Sonsonate&lt;/span&gt;
 &lt;span class="n"&gt;La&lt;/span&gt; &lt;span class="n"&gt;Libertad&lt;/span&gt;
 &lt;span class="n"&gt;Santa&lt;/span&gt; &lt;span class="n"&gt;Ana&lt;/span&gt;
 &lt;span class="n"&gt;La&lt;/span&gt; &lt;span class="k"&gt;Union&lt;/span&gt;
 &lt;span class="n"&gt;Cuscatlan&lt;/span&gt;
 &lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="n"&gt;Vicente&lt;/span&gt;
 &lt;span class="n"&gt;La&lt;/span&gt; &lt;span class="n"&gt;Paz&lt;/span&gt;
 &lt;span class="n"&gt;Usulutan&lt;/span&gt;
 &lt;span class="n"&gt;Morazan&lt;/span&gt;
 &lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="n"&gt;Miguel&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, what happens if the user is looking for &lt;code&gt;San Salvador&lt;/code&gt; but it misspells it and write &lt;code&gt;sn salvador&lt;/code&gt;? Our system should recommend the user to write it properly! How? Let's see:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;t_departments&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="s1"&gt;'sn salvador'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="n"&gt;name&lt;/span&gt;     
&lt;span class="c1"&gt;--------------&lt;/span&gt;
 &lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="n"&gt;Salvador&lt;/span&gt;
 &lt;span class="n"&gt;Santa&lt;/span&gt; &lt;span class="n"&gt;Ana&lt;/span&gt;
 &lt;span class="n"&gt;San&lt;/span&gt; &lt;span class="n"&gt;Miguel&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;And 🎉. By returning the first result, we can tell the user that we know what he was trying to write, and still, return results related to its search, as Google does!&lt;/p&gt;

&lt;h3&gt;
  
  
  Solving our initial problem
&lt;/h3&gt;

&lt;p&gt;Well, our initial problem was the user, right? Jk, our initial problem was to handle misspellings when users type a name, but they still want a result. &lt;br&gt;
It's now trivial to do this with our new knowledge, by querying either the &lt;code&gt;word similarity&lt;/code&gt; or the &lt;code&gt;distance&lt;/code&gt; between the words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;unnest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;array&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'jonh doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'jane doe'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'mark twain'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'some user'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'a person'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'tommy shelby'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;names&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="s1"&gt;'doe jonh'&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;names&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;names&lt;/span&gt;    
&lt;span class="c1"&gt;------------&lt;/span&gt;
 &lt;span class="n"&gt;jonh&lt;/span&gt; &lt;span class="n"&gt;doe&lt;/span&gt;
 &lt;span class="n"&gt;jane&lt;/span&gt; &lt;span class="n"&gt;doe&lt;/span&gt;
 &lt;span class="n"&gt;mark&lt;/span&gt; &lt;span class="n"&gt;twain&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Ignore Mark Twain...&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I hope you liked the article, and if you are curious and want to delve more, you can read the &lt;a href="https://www.postgresql.org/docs/9.6/pgtrgm.html" rel="noopener noreferrer"&gt;whole documentation&lt;/a&gt; for trigrams and all the operators you can. In my next blog post, I'll share how to index this kind of data so the queries are faster!&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Getting query performance stats with PostgreSQL</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Wed, 29 Apr 2020 16:11:51 +0000</pubDate>
      <link>https://dev.to/kaleman15/getting-query-performance-stats-with-postgresql-48po</link>
      <guid>https://dev.to/kaleman15/getting-query-performance-stats-with-postgresql-48po</guid>
      <description>&lt;p&gt;In this article, you will see how to use some hidden features of PostgreSQL to get useful insight into your running queries in PostgreSQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Have you tried to spot performance problems in your application? Maybe some of them live in the code (a map over thousands of elements maybe...) or maybe the performance issue is caused by another factor: badly defined SQL queries.&lt;/p&gt;

&lt;p&gt;As a developer, you will have to deal with SQL someday, maybe sooner than later. And probably you will have to work with queries that other people made, or even with queries that the &lt;em&gt;you&lt;/em&gt; from the past created. &lt;/p&gt;

&lt;p&gt;The problem is that, without the right tool and the right information, is very difficult to identify a slow query. Why?&lt;/p&gt;

&lt;h3&gt;
  
  
  Some queries are slower with more data
&lt;/h3&gt;

&lt;p&gt;For example, imagine a simple query that joins multiple tables. In your local, with probably 10 users the query won't perform bad (and if it is, it is easier to spot it!). &lt;/p&gt;

&lt;h3&gt;
  
  
  Some queries needs an index
&lt;/h3&gt;

&lt;p&gt;Indexing is probably the root cause of performance issues. The lack of them, as well as the presence of them, can cause problems. With a small set of data, you won't be able to see if a query needs an index or not. Even worse (or better, it depends) PostgreSQL can leave the index out of the query if the dataset is small enough to do a sequential scan (this is, row by row).&lt;/p&gt;

&lt;p&gt;Without having the issue happening in a production environment, is very difficult to spot issues like this one, and there's a big possibility of the end-user spotting them before you.&lt;/p&gt;

&lt;p&gt;This approach (waiting for the user to tell you the app is slow) is very reactive. You have to wait until the problem happens before working on a fix for the issue. But, what about being able to have this information &lt;em&gt;before&lt;/em&gt; the issue happens?&lt;/p&gt;

&lt;p&gt;For that scenario is the reason why some PostgreSQL views exist. Those maintenance views are a gold mine for developers that want to track the performance of their queries. Let's talk more about them!&lt;/p&gt;

&lt;h2&gt;
  
  
  The solution: PostgreSQL maintenance views
&lt;/h2&gt;

&lt;p&gt;PostgreSQL has a lot of views for this purpose. Some of them give us stats about Disk I/O and network statistics. Others allow us to see replication stats and things like that. Here, we'll talk about 3 views that can help you nail down query issues: &lt;code&gt;pg_stat_user_tables&lt;/code&gt;, &lt;code&gt;pg_stat_user_indexes&lt;/code&gt; and &lt;code&gt;pg_stat_statements&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pg_stat_user_tables&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This view shows you statistics about each table per schema (there's one row per table) and gives you information like the number of &lt;code&gt;sequential scans&lt;/code&gt; that PG has performed in the table, how much &lt;code&gt;select/insert&lt;/code&gt; operations are done in it and so on&lt;/p&gt;

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

&lt;p&gt;As you can see here, for the first row, it has performed 1 &lt;code&gt;sequential scan&lt;/code&gt; and that scan returned 939 rows. There were 2 index scans and they returned 2 rows. Numbers are low because I'm using a local database, but these numbers should be larger in a production database.&lt;/p&gt;

&lt;p&gt;From this view, apart from all the useful information, we can answer something really interesting: &lt;em&gt;which of my tables need an index?&lt;/em&gt; You can easily answer this question by querying the &lt;code&gt;seq_scan&lt;/code&gt; and &lt;code&gt;seq_tup_read&lt;/code&gt; columns!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; 
  &lt;span class="n"&gt;schemaname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;seq_scan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;seq_tup_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;seq_tup_read&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;seq_scan&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;avg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;idx_scan&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_stat_user_tables&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;seq_scan&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;seq_tup_read&lt;/span&gt; &lt;span class="k"&gt;desc&lt;/span&gt; &lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running that query will return us the following&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff2vpvg9pkog0m0j67621.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Ff2vpvg9pkog0m0j67621.png" alt="pg_stat_user_tables query result" width="769" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, it is suggesting me to add an index to those tables since they were used recently in sequential scans. With more data and more runtime, this query will give you a good insight on how your tables are behaving.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pg_stat_user_indexes&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Even when adding indexes solves many problems, they are not the holy grail, and they come with a cost: space. Indexes are good, yes, we all concord in that. But something worse than not having an index is to have a useless one. Why? Well, first of all, it will take space from your DB server. Indexes on big tables can be very expensive, and get very very big. The second reason is that the index should be recalculated each time you write to the table. And of course, recalculating a useless index is like paying for food you won't eat!&lt;/p&gt;

&lt;p&gt;So, every time you add an index, make sure it makes sense. &lt;/p&gt;

&lt;p&gt;But, what happens if you work on a codebase &amp;amp; DB schema that you didn't design? Is this the world's end? Absolutely! PostgreSQL views to the rescue again! The &lt;code&gt;pg_stat_user_indexes&lt;/code&gt; table is capable of showing you the &lt;em&gt;frequency of use&lt;/em&gt; of your indexes, alongside with the space they are taking. &lt;/p&gt;

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

&lt;p&gt;As you can see from the image above, some of my primary keys weren't used yet. But, this doesn't give us too much detail yet. Because we don't know what's the space in disk our index is using! We can get that information by using the &lt;code&gt;pg_relation_size&lt;/code&gt; function with the &lt;code&gt;indexrelid&lt;/code&gt; from our results.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;schemaname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;relname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indexrelname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; 
&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexrelid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;idx_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;pg_size_pretty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pg_relation_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexrelid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;idx_scan&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;indexrelid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_stat_user_indexes&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2qx9yf0v759pe1t9wrtc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2qx9yf0v759pe1t9wrtc.png" alt="pg_stat_user_indexes query result" width="800" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The result of this query shows you indexes that hadn't been used in a while alongside with its space consumption. This can give you an idea of which indexes to take care of.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As a note, the result of this query doesn't mean that you should drop all of those indexes that hadn't been used. You should always investigate why the index isn't being used before deleting it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;pg_stat_statements&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This is probably the most useful one. It's hard for me to understand why this view isn't enabled by default! This view should be &lt;em&gt;enabled&lt;/em&gt; in the PostgreSQL conf to be used by you.&lt;/p&gt;

&lt;h4&gt;
  
  
  Activate
&lt;/h4&gt;

&lt;p&gt;To enable this view, we should include it into the &lt;code&gt;shared_preload_libraries&lt;/code&gt; list. Since I'm using Docker &amp;amp; Docker Compose to manage my database, I can easily add an option to the start command to be like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;postgres&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:10&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=${PG_PASSWORD:-postgres}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PGDATA='/var/lib/postgresql/data'&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;postgres"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shared_preload_libraries=pg_stat_statements"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After this, when you start PostgreSQL again the library will be loaded with the DBMS&lt;/p&gt;

&lt;h4&gt;
  
  
  Create extension
&lt;/h4&gt;

&lt;p&gt;After enabling the library, you should enable it as an extension. You can do this by running the following query&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;extension&lt;/span&gt; &lt;span class="n"&gt;pg_stat_statements&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If that query doesn't give you an error, you are good go! Let's confirm this by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_stat_statements&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;From this view, we can get very good information about how our queries perform. For example, we have the number of &lt;code&gt;calls&lt;/code&gt; a given query had. The &lt;code&gt;mean_time&lt;/code&gt; of execution between all the calls and even the &lt;code&gt;stddev_time&lt;/code&gt; (Standard Deviation) of the calls, to see if the queries have a consistent runtime or they vary and how much they do. &lt;/p&gt;

&lt;p&gt;From this view, you can even know how many rows a query returned if those rows came from the cache or from disk and so on!&lt;/p&gt;

&lt;p&gt;With all of this information, it's easy to get a list of the most expensive queries and get to know why&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;total_time&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="k"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_time&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;over&lt;/span&gt; &lt;span class="p"&gt;())::&lt;/span&gt;&lt;span class="nb"&gt;numeric&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="n"&gt;percent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;total_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&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="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mean_time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;numeric&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="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;mean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="n"&gt;stddev_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
             &lt;span class="k"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;query&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;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_stat_statements&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;total_time&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;limit&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F32t84yfy8zmeg72u86rw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F32t84yfy8zmeg72u86rw.png" alt="pg_stat_statements query result" width="800" height="224"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With that query, you now have a list of the 10 most expensive queries, how much time they used, how many times they have been called and the deviation from the mean-time those queries have. &lt;/p&gt;

&lt;p&gt;Using this, you can track what queries are the ones that take more time and try to fix them (or understand at least why they perform like that).&lt;/p&gt;

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

&lt;p&gt;Using PostgreSQL to monitor PostgreSQL is very useful and can point you in the right place to understand the performance of your application and the pain points it has. &lt;/p&gt;

&lt;p&gt;I hope you liked the article and learned something from it! &lt;/p&gt;

&lt;p&gt;Note: This article was also published in &lt;a href="https://kaleman.netlify.app/getting-query-performance-stats-with-pg" rel="noopener noreferrer"&gt;my blog&lt;/a&gt;. I'm still trying to find a good domain name for it lol&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>database</category>
      <category>sql</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>How-to: Handle schedules with PostgreSQL</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Wed, 01 Apr 2020 22:32:48 +0000</pubDate>
      <link>https://dev.to/kaleman15/how-to-handle-schedules-with-postgresql-ga2</link>
      <guid>https://dev.to/kaleman15/how-to-handle-schedules-with-postgresql-ga2</guid>
      <description>&lt;p&gt;How many times you tried to build a scheduling system and found too complex to arrange the dates in a way there are no collisions? Nobody? Only me?&lt;br&gt;
Well, in this article you'll learn an easy way to handle scheduling by using some of the hidden features of PostgreSQL!&lt;/p&gt;

&lt;p&gt;In case you don't know, PostgreSQL is a fully-featured and open source DB Engine written mostly in C. PostgreSQL contains a ton of features that can make our lives easier, I'll try to cover some of them in other articles. &lt;/p&gt;

&lt;p&gt;In this article, we'll talk about 3 of those powerful features: GiST indexes, Exclusion operators &amp;amp; ts-ranges.&lt;/p&gt;

&lt;p&gt;Let's talk about them a little to have context:&lt;/p&gt;
&lt;h3&gt;
  
  
  GiST indexes
&lt;/h3&gt;

&lt;p&gt;A GiST index, or a &lt;code&gt;Generalized Search Tree&lt;/code&gt; is a very important type of index. They are a "balanced, tree-structured access method that acts as a base template in which to implement arbitrary indexing schemes" according to the &lt;a href="https://www.postgresql.org/docs/9.5/gist-intro.html" rel="noopener noreferrer"&gt;Postgres documentation&lt;/a&gt; about them. They are like the "b-trees" that you may have heard of. The difference between them is the operations they support. For example, a "b-tree" supports the standard comparisons between elements in the tree (&amp;lt;,&amp;gt;,=), but, for some types of data (geospatial, text, images) this kind of comparisons doesn't make sense!&lt;br&gt;
This is when GiST saves the day. They allow us to customize the way the data is stored &amp;amp; balanced in the tree so we can perform another kind of operation over the indexed data, for example, calculate overlapping between the data, intersections, containment, etc.&lt;/p&gt;

&lt;p&gt;However, they are not enabled by default (at least in my compilation) so, you have to enable them by "creating an extension". Create an extension is basically install it from a predefined source. You can create your own extensions but for this example, we'll be using a predefined one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;EXTENSION&lt;/span&gt; &lt;span class="n"&gt;btree_gist&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can delve into how they work in &lt;a href="https://medium.com/postgres-professional/indexes-in-postgresql-5-gist-86e19781b5db" rel="noopener noreferrer"&gt;this blog post&lt;/a&gt; that explains the topic really well!&lt;/p&gt;

&lt;h3&gt;
  
  
  TS-Ranges
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;ts-range&lt;/code&gt; or Timestamp Range is a kind of Range datatype in Postgres. This datatype will create a "range" (surprise!) between the 2 dates passed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;tsrange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'2020-01-01'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'2020-01-02'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

                    &lt;span class="n"&gt;tsrange&lt;/span&gt;                    
&lt;span class="c1"&gt;-----------------------------------------------&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;"2020-01-01 00:00:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"2020-01-02 00:00:00"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If curious about what the &lt;code&gt;[ )&lt;/code&gt; on the range means, you can look at Postgres' &lt;a href="https://www.postgresql.org/docs/9.3/rangetypes.html" rel="noopener noreferrer"&gt;official documentation&lt;/a&gt; about ranges.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tsranges&lt;/code&gt; doesn't have a timezone by default. If you want to include the timezone in your ranges you can use the &lt;code&gt;tszrange&lt;/code&gt; datatype. &lt;/p&gt;

&lt;h3&gt;
  
  
  Exclusion operators
&lt;/h3&gt;

&lt;p&gt;Indexes are widely used to speed things up and for uniqueness. However, do you know that you can use indexes to define additional constraints? For example, scheduling constraints. As I mentioned earlier, you can determine if 2 pieces of data overlap using GiST indexes. What we'll be doing is to use that in our favor!&lt;br&gt;
Exclusion operators are easy to define, you just state you want to use them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;from_to&lt;/span&gt; &lt;span class="n"&gt;tsrange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;EXCLUDE&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GiST&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_to&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;Basically, what we're saying is: "hey Postgres, create an exclusion rule for this table. And, &lt;em&gt;when adding new elements&lt;/em&gt; use the &lt;code&gt;=&lt;/code&gt; operator to compare &lt;code&gt;room_id&lt;/code&gt;s and use &lt;code&gt;&amp;amp;&amp;amp;&lt;/code&gt; (overlap) operator to compare the &lt;code&gt;from_to&lt;/code&gt; tsrange."&lt;/p&gt;

&lt;p&gt;Now that we have the table created, It's time to test!!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create the test table we're going to use (in case you didn't)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;from_to&lt;/span&gt; &lt;span class="n"&gt;tsrange&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;EXCLUDE&lt;/span&gt; &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="n"&gt;GiST&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;room_id&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;from_to&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&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;Let's book some rooms!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="k"&gt;VALUES&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="s1"&gt;'["2020-01-01", "2020-01-10"]'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="k"&gt;VALUES&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="s1"&gt;'["2020-01-01", "2020-01-15"]'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="k"&gt;VALUES&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="s1"&gt;'["2020-01-06", "2020-01-12"]'&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;Test our exclusion rule by inserting a reservation on a scheduled period.
Visually, this is what we're trying to do when inserting the new schedule:
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fltfzki4bblvillv4va95.png" alt="Current schedule" width="800" height="238"&gt;
As you can see, the period in red &lt;code&gt;overlaps&lt;/code&gt; with the current booked period, so it should fail. Let's try it!
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;reservations&lt;/span&gt; &lt;span class="k"&gt;VALUES&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="s1"&gt;'["2020-01-06", "2020-01-15"]'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And what happens when we try to insert that scheduling?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR:  conflicting key value violates exclusion constraint "reservations_room_id_from_to_excl"
DETAIL:  Key (room_id, from_to)=(1, ["2020-01-06 00:00:00","2020-01-15 00:00:00"]) conflicts with existing key (room_id, from_to)=(1, ["2020-01-01 00:00:00","2020-01-10 00:00:00"]).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means our implementation is correct! 🎉&lt;br&gt;
Since we're using timestamps, you can be even more specific by specifying minutes, seconds and so on and Postgres will also take care of them!&lt;/p&gt;

&lt;p&gt;I hope you learned something new! And, if you already knew about those features but didn't know you could use them for this, there you have it!&lt;/p&gt;

&lt;p&gt;Hope you liked the post, bye! &lt;/p&gt;

&lt;p&gt;Disclaimer: this may not work for all the scenarios.&lt;/p&gt;

</description>
      <category>postgres</category>
      <category>programming</category>
      <category>database</category>
      <category>webdev</category>
    </item>
    <item>
      <title>A tale of Candy Crush &amp; Technical debt</title>
      <dc:creator>Kevin Alemán</dc:creator>
      <pubDate>Mon, 30 Mar 2020 03:50:17 +0000</pubDate>
      <link>https://dev.to/kaleman15/a-tale-of-candy-crush-technical-debt-143h</link>
      <guid>https://dev.to/kaleman15/a-tale-of-candy-crush-technical-debt-143h</guid>
      <description>&lt;p&gt;One day, when I was playing Candy Crush (Soda Saga, to be specific) I came across a very interesting but difficult level. Personally, I hate all the Chocolate-related levels on the game, but, well, a game is a game. &lt;br&gt;
In case you had never played Candy Crush before, this is what I'm talking about: &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%2Fi%2Fax28z62367a253mn3tth.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fax28z62367a253mn3tth.jpg" alt="A candy crush chocolate level" width="600" height="447"&gt;&lt;/a&gt;&lt;br&gt;
The objective in those levels is to destroy all the chocolate on the screen with the movements you have. This is not so difficult until you discover that when in a move you don't destroy any chocolate block, they multiply leaving you with less space to move, converting a peaceful level in a kind of race where you have to keep destroying or end up flooded in chocolate.&lt;/p&gt;

&lt;p&gt;This situation made me think about a real-life scenario: Agile projects &amp;amp; technical Debt. So, how does technical debt assimilates to one of those levels? Let's discover!&lt;/p&gt;
&lt;h2&gt;
  
  
  Chocolate multiplies as well as Technical Debt
&lt;/h2&gt;

&lt;p&gt;First of all, let's state something: it's almost impossible to have an agile project without having technical debt at some point.&lt;br&gt;
Having said that, let's think about chocolate. You start a level with a predefined amount of chocolate. If you don't do anything to clear them quickly, they will increase in number to the point where all on your screen is chocolate.&lt;br&gt;
The same happens with tech debt. If your project doesn't have a plan to reduce the amount of TD, your entire project will convert into it and this will leave you in a point where it is easier to rebuild everything from scratch than fix the existing code base.&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%2Fi%2F9s3fbvh9881z9yga483s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F9s3fbvh9881z9yga483s.png" alt="An screen almost full of chocolate blocks" width="670" height="652"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  If you don't have a strategy, you will lose.
&lt;/h2&gt;

&lt;p&gt;That kind of levels have something in common: you should think your movements before doing them. &lt;br&gt;
When debating about tech debt the same happens: You should know what you're doing. For example, if someone tells you that to reduce tech debt you should refactor every piece of code, yes, that could reduce tech debt, but at the same time, you are removing battle-tested code with something that probably won't work for all cases the old code did, introducing new tech debt &amp;amp; bugs. On the other hand, if the tech debt is not in the code per se, but on the way the project is architectured, you can end with more troubles than solutions by blindly changing current architecture. So, you should have a plan to fix tech debt, or you'll end up in a worse state.&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%2Fi%2Fxqpro3anpibs5a3ms1og.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxqpro3anpibs5a3ms1og.jpg" alt="Game over screen" width="280" height="373"&gt;&lt;/a&gt;&lt;br&gt;
Good strategies to reduce the Tech debt in a project are described here in this DZone's &lt;a href="https://dzone.com/articles/how-to-reduce-tech-debt-a-practical-experience-gui-1" rel="noopener noreferrer"&gt;blog post&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  There are pieces of chocolate harder than others
&lt;/h2&gt;

&lt;p&gt;As in real life, there are pieces of code, architecture decisions, product deadlines that are very difficult (or nearly impossible) to change! Let's write a dummy piece of code that will represent a tech debt:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;evaluateUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&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="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;act&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&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;isNew&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&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;company&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fancy company&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="cm"&gt;/* do something really difficult 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;Maybe there was a project deadline that the team had to meet so they decided to hard-code the company's name. And this works! Until the time the client says: &lt;code&gt;I want users from the company&lt;/code&gt;"bla"&lt;code&gt;to be excluded too, but only new users&lt;/code&gt;. As easy as this change may look, the team should be careful not to affect how the users from &lt;code&gt;"fancy company"&lt;/code&gt; are evaluated and, if possible, make changes to this piece of code easier in the future (pay the tech debt). This situation makes changes to take longer and to be more error-prone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Chocolate is not always bad
&lt;/h2&gt;

&lt;p&gt;When talking about tech debt, some people tend to touch it with a stick from the distance, avoiding all contact like if tech debt was the plague. But, that's not realistic. We all have tech debt. In this topic, I really like Squarespace's &lt;a href="https://engineering.squarespace.com/blog/2019/three-kinds-of-good-tech-debt" rel="noopener noreferrer"&gt;blog post&lt;/a&gt; regarding good types of tech debt. In summary, tech debt is good when you know it is there. When it's intentional. Because that kind of tech debt can be paid, the one you are aware of. You can't pay what you don't know you owe!!&lt;br&gt;
So, evergreen advice for dealing with this is to always document what your tech debt is, keep that document updated &amp;amp; involve your management team in this!&lt;/p&gt;

&lt;p&gt;In conclusion, if I was able to pass that level, you can pay your technical debt!!&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%2Fi%2Fgqa0s5oaa2adfgjw90qu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgqa0s5oaa2adfgjw90qu.png" alt="You won! Screen" width="800" height="559"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>techdebt</category>
      <category>scrum</category>
      <category>agile</category>
    </item>
  </channel>
</rss>
