<?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: Dmitry Kudryavtsev</title>
    <description>The latest articles on DEV Community by Dmitry Kudryavtsev (@skwee357).</description>
    <link>https://dev.to/skwee357</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%2F892468%2F19ab5360-0f48-46b7-8fa7-23d34a5cb0e9.jpg</url>
      <title>DEV Community: Dmitry Kudryavtsev</title>
      <link>https://dev.to/skwee357</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skwee357"/>
    <language>en</language>
    <item>
      <title>Why engineers should focus on writing</title>
      <dc:creator>Dmitry Kudryavtsev</dc:creator>
      <pubDate>Mon, 24 Jul 2023 22:27:16 +0000</pubDate>
      <link>https://dev.to/skwee357/why-engineers-should-focus-on-writing-145b</link>
      <guid>https://dev.to/skwee357/why-engineers-should-focus-on-writing-145b</guid>
      <description>&lt;p&gt;&lt;em&gt;This essay was originally published in my blog - &lt;a href="https://www.yieldcode.blog/post/why-engineers-should-write/"&gt;yield code();&lt;/a&gt; I'm a big believer in owning your content, instead of publishing it to corporations who might charge their readers for it. Please consider subscribing to my blog. Not all the articles I post there, will be reshared on other platforms.&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;All engineers are good writers… of code. But I believe that in order to a become better engineer–you should improve your writing skills.&lt;/p&gt;

&lt;p&gt;From the dawn of times, people were writing. We have written using symbols, like in Ancient Egypt. And we have written using letters, like in Renaissance times. And all of us, got at least one writing assignment in school, without the “Why?” And yet, today writing is so underrated, that most people want to avoid it. But the truth is–you will have to write. Comments, documentation, design documents, presentations. Whether you like it or not. So why not become better at it?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why write?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Writing is a way to organize your brain
&lt;/h3&gt;

&lt;p&gt;For some reason we, humans, think that knowledge equals reading stuff. I’ve read the starting guide for Rust, so now I know this topic. I’ve read a book about how TCP/IP works–so now I know this stuff. But it’s not true. If it was true, we’d all be super-stars.&lt;/p&gt;

&lt;p&gt;What helps us cement our knowledge–is writing. That’s why I believe in writing code, as opposed to copying code snippets. Because when you type it–you cement that knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing is a way to learn something
&lt;/h3&gt;

&lt;p&gt;If you want to learn a new topic–write about it. When I want to learn a new programming language, I write a short program using that programming language. When I want to understand how something works–I write an article on this blog about it.&lt;/p&gt;

&lt;p&gt;Real writing, where you try to dig the truth, will require you to try and experiment. I’ve learned more by writing articles on this blog, than by reading programming books.&lt;/p&gt;

&lt;h3&gt;
  
  
  Writing helps you identify mistakes
&lt;/h3&gt;

&lt;p&gt;How many times we are asked to prepare a design document, while we think to ourselves, “What for? The design is so simple, I can hold it in my head easily.” This is a big misconception, we all fall for. If we would actually write the design document, we would have identified so many problems with our “simple” design. Things like–inconsistencies, missing details, or simply “lazy” thinking our brain did, which in reality make no sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to write more?
&lt;/h2&gt;

&lt;p&gt;Remember–reading is a habit, writing is a skill. And in order to prefect your skill, you need to write more. One simple way to write more–is to approach design reviews differently. Instead of hating them, and doing them like a homework assignment, try to approach them with enthusiasm. Each time you write a design review, try to improve something. Make it shorter–without missing the point. Make it longer–in order to cover more use-cases.&lt;/p&gt;

&lt;p&gt;Writing design reviews, and documentations–at your workplace–is an easy way to get into writing. You will have to do it anyway, so why not improve while writing? However, if you want to improve your writing further, outside your workplace, consider starting a blog. If writing a blog is intimidating for you, consider answering questions on Stackoverflow–but focus on providing textual content, rather than copy-pasting code snippets.&lt;/p&gt;

&lt;p&gt;One last advice–abolish the copy-paste. So many developers, whom I mentored, simply copy-paste everything. Code snippets, function declarations, etc. I know how to initialize a git repository, because I do it by hand every time. Most people simply copy the instructions from GitHub or Google. And if you are scared about being unproductive–remember that you are not judged by how much code you write, or how fast you complete the assignment.&lt;/p&gt;




&lt;p&gt;Writing is unavoidable. Whether you like it or not, if you want to become a 10x engineer, you will have to write. Write documentations, write design reviews, write presentations. The sooner you get better at writing, the faster you will become a better engineer.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Cursor based pagination</title>
      <dc:creator>Dmitry Kudryavtsev</dc:creator>
      <pubDate>Sat, 21 Jan 2023 13:56:20 +0000</pubDate>
      <link>https://dev.to/skwee357/cursor-based-pagination-njo</link>
      <guid>https://dev.to/skwee357/cursor-based-pagination-njo</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was originally published in my &lt;a href="https://www.yieldcode.blog/post/cursor-based-pagination"&gt;Personal Blog&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Traditional pagination existed for many years. But it’s not useful for platforms with huge amount of user generated content. Luckily, there is an alternative solution for such systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traditional pagination
&lt;/h2&gt;

&lt;p&gt;Before we jump into the solution, let’s look at traditional pagination and it’s drawbacks. Let’s say we have a database table for forum posts that looks like this:&lt;br&gt;
| ID  | Title     | Content      | LastActiveDate      |&lt;br&gt;
| --- | --------- | ------------ | ------------------- |&lt;br&gt;
| 1   | Some post | Some content | 2022-11-26T14:00:00 |&lt;/p&gt;

&lt;p&gt;In this case, &lt;code&gt;LastActiveDate&lt;/code&gt;—indicates the date when the post was last active (i.e., a comment was published).&lt;/p&gt;

&lt;p&gt;We want to display a front page with all the posts sorted by &lt;code&gt;LastActiveDate&lt;/code&gt;. Since we don’t know how much data we can have, we decide on an arbitrary &lt;strong&gt;page size&lt;/strong&gt; of 10. And then, in order to display the posts on a particular page, we will use 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;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;posts&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;LastActiveDate&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;{(&lt;/span&gt;&lt;span class="n"&gt;currentPage&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The math is very simple. Given a specific page, say &lt;strong&gt;2&lt;/strong&gt;, we will select all items starting from offset &lt;code&gt;(2 - 1) * 10 = 10&lt;/code&gt;, limiting the selection to 10 elements, and the result will be all the items in group of &lt;code&gt;[10,20)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Usually, with traditional pagination, we also need to have another query in order to generate the visual pagination itself. And for that we need to know the total amount of pages we have, which can be done by:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SELECT count(ID) FROM posts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalPages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;totalItems&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;pageSize&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Drawbacks
&lt;/h3&gt;

&lt;p&gt;This pagination style, however, has one major drawback. Let’s say I’ve navigated to page 1 of the forum and then decided to go make some tea. After coming back and seeing that no posts on the first page are of interest to me, I navigate to the second page and find out that it includes 2 posts from the first page.&lt;/p&gt;

&lt;p&gt;This happens because during the time I was making and drinking my tea, some older posts that were not among the 10 posts on the first page — were updated, hence changing their &lt;code&gt;LastActiveDate&lt;/code&gt; to be the most recent. Upon navigating to the second page, I now see 10 more posts, but because of the shift in order, 2 posts from the first 10 — were pushed into the second 10 on the second page.&lt;/p&gt;

&lt;p&gt;Usually, on platforms with low update rates, like a small forum — this drawback is not that critical. Occasionally users will see content on the next page, which appeared on the previous page — but the frequency during which it happens — is low. We can increase the page size to say 15, 20, 25, or even 30 posts per page — and since forums often have activity concentrated in few popular or new posts on the first page, the chance of seeing the same data on next pages — is small, and we can live with that.&lt;/p&gt;

&lt;p&gt;However, for high throughput platforms like Twitter, or machine-to-machine streaming services — this case is unacceptable. Image navigating Twitter only to see the same content over and over again, because the rate at which content is created on Twitter is rapid, and thus content from the previous page is always pushed down to the next page.&lt;/p&gt;

&lt;p&gt;In order to solve this problem, let me introduce — Cursor based pagination.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cursor based pagination
&lt;/h2&gt;

&lt;p&gt;Let’s say we want our forum pagination to never display data from previous pages. When a user navigates to the second page — she will always see data that suppose to be on that page. This can be achieved with cursor based pagination.&lt;/p&gt;

&lt;h3&gt;
  
  
  How cursor based pagination works
&lt;/h3&gt;

&lt;p&gt;In cursor based pagination, we do not have logical pages. Instead, each page returns the data that suppose to be displayed on that page as well as a &lt;strong&gt;token&lt;/strong&gt; to retrieve the next page. The token is usually opaque, and for the end user it has no meaning — but our system knows to read it and generate the next page based on it.&lt;/p&gt;

&lt;p&gt;This brings me to the first drawback of cursor based pagination:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!attention] &lt;br&gt;
Cursor based pagination is &lt;strong&gt;forward&lt;/strong&gt;. It does not support going back (i.e. navigating from second page back to first, or from third back to second); nor it supports &lt;em&gt;skipping&lt;/em&gt; pages (i.e. going to page 5 from page 2).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;On a platform like forum, this might not be very useful. Forums are typically meant to be &lt;em&gt;browsed&lt;/em&gt;, so users always can jump to a specific page, navigate backwards and forwards, or event jump to the last page. However, — on platforms with &lt;em&gt;infinite&lt;/em&gt; scrolling navigation, like feeds, this solution is perfect.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cursor based pagination example
&lt;/h3&gt;

&lt;p&gt;Let’s say our product manager came up with the terrible idea of turning our 90s looking forum with traditional pagination, to 2000s looking forum with infinite scroll feed. We’ve tried to warn him that this is a bad idea, but he insisted that users wanted it. And so let’s turn our forums’ traditional pagination — to cursor based instead.&lt;/p&gt;

&lt;p&gt;First, we need to select the cursor. Since forum posts are often sorted in chronological order from newest to oldest post, it makes sense to use the &lt;code&gt;LastActiveDate&lt;/code&gt; as a cursor. Hence, the retrieval of a particular pages’ data can be done like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getPosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;table&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;posts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;orderBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LastActiveDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DESC&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PAGE_SIZE&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;q&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;q&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LastActiveDate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cursor&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;nextPageCursor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;posts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LastActiveDate&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="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Disclaimer: The query builder is a pseudocode, but can be achieved with tools like &lt;a href="https://knexjs.org/"&gt;KnexJS&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For every call, we fetch the first &lt;code&gt;PAGE_SIZE&lt;/code&gt; posts and provide a cursor to the next page, which is equal to the &lt;code&gt;LastActiveDate&lt;/code&gt; of the last post in our current batch (or &lt;code&gt;null&lt;/code&gt; if no posts exist). If the &lt;code&gt;cursor&lt;/code&gt; is &lt;code&gt;null&lt;/code&gt; — we start from the first page.&lt;/p&gt;

&lt;p&gt;Since our posts are always sorted in chronologically descending order, our pagination becomes consistent.&lt;/p&gt;

&lt;p&gt;Let’s try to do a visualization. Let’s imagine we have the following database:&lt;br&gt;
| ID  | Title   | ….  | LastActiveDate      |&lt;br&gt;
| --- | ------- | --- | ------------------- |&lt;br&gt;
| 1   | Title 1 |     | 2022-01-10T00:00:00 |&lt;br&gt;
| 2   | Title 2 |     | 2022-01-09T00:00:00 |&lt;br&gt;
| 3   | Title 3 |     | 2022-01-08T00:00:00 |&lt;br&gt;
| 4   | Title 4 |     | 2022-01-07T00:00:00 |&lt;br&gt;
| 5   | Title 5 |     | 2022-01-06T00:00:00 |&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[!disclaimer]&lt;br&gt;
I’ve intentionally sorted the posts by &lt;code&gt;LastActiveDate&lt;/code&gt; to easier visualize the process. Obviously, the data does not need to be sorted in advance, since SQLs &lt;code&gt;SORT BY LastActiveDate DESC&lt;/code&gt; handles the sorting.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the sake of simplicity, let’s set our page size to 2. Upon first execution, we will get posts 1 and 2 and our &lt;code&gt;nextPageCursor&lt;/code&gt; will be equal to &lt;code&gt;2022-01-09T00:00:00&lt;/code&gt; (which is the &lt;code&gt;LastActiveDate&lt;/code&gt; of the second post).&lt;/p&gt;

&lt;p&gt;As we would like to retrieve the next page, we will provide the &lt;code&gt;nextPageCursor&lt;/code&gt; to our function, and since the query looks at posts older than &lt;code&gt;2022-01-09T00:00:00&lt;/code&gt; we will get two more posts — IDs 3 and 4.&lt;/p&gt;

&lt;p&gt;Now — let's come back to our tea scenario. Imagine I’m looking at the forum's first page with posts 1 and 2, and before clicking &lt;em&gt;next&lt;/em&gt;, I go to drink tea. During that time, someone posts a new comment in post ID 3 — thus altering its &lt;code&gt;LastActiveDate&lt;/code&gt; to be today (let’s say today is &lt;code&gt;2022-01-11T00:00:00&lt;/code&gt;). The table will now look like this:&lt;br&gt;
| ID  | Title   | ….  | LastActiveDate      |&lt;br&gt;
| --- | ------- | --- | ------------------- |&lt;br&gt;
| 3   | Title 3 |     | 2022-01-11T00:00:00 |&lt;br&gt;
| 1   | Title 1 |     | 2022-01-10T00:00:00 |&lt;br&gt;
| 2   | Title 2 |     | 2022-01-09T00:00:00 |&lt;br&gt;
| 4   | Title 4 |     | 2022-01-07T00:00:00 |&lt;br&gt;
| 5   | Title 5 |     | 2022-01-06T00:00:00 |&lt;/p&gt;

&lt;p&gt;When I come back to the computer, I click &lt;em&gt;next page&lt;/em&gt;. In our traditional navigation — my action would have returned posts with IDs 2 and 4, thus “leaking” the second post from the first page to the second.&lt;/p&gt;

&lt;p&gt;Cursor based navigation, with the cursor of &lt;code&gt;2022-01-09T00:00:00&lt;/code&gt;, would however return posts with IDs 4 and 5 as expected. Notice how post ID 3 is skipped and will never be seen by me, unless I start over from page 1. It’s important to point out that this flaw is not a problem of the cursor based pagination — but rather a problem with the way we’ve &lt;em&gt;implemented&lt;/em&gt; cursor based pagination. Different use cases — have different implementations. It is possible to implement cursor base pagination which takes into account newer content as well. It, however, makes little sense to do so in a context of a forum, since in forums — going forward in pagination — means seeing older content.&lt;/p&gt;

&lt;p&gt;As I’ve said before, we do lose some functionality — namely: going backwards (from page 2 to page 1 for example) and skipping pages (from page 2 to page 7 for example). So this approach is not useable for &lt;em&gt;browsing&lt;/em&gt; content in a paginated manner, but it is a good approach to implement infinite scrolling without encountering duplicate content.&lt;/p&gt;
&lt;h3&gt;
  
  
  Bonus—don’t use dates and or timestamps as cursor
&lt;/h3&gt;

&lt;p&gt;I could have ended the article here, but I’m pretty sure if I did so — I’d get criticized for using date as a cursor. And this will be a well deserved criticism.&lt;/p&gt;

&lt;p&gt;You see — the entire point of cursor based navigation — is to create a consistent stream of data without repetitions. Yes — you give away some features, but in exchange you get fresh data for each page. As I’ve said — some systems might benefit from it.&lt;/p&gt;

&lt;p&gt;However, — by using dates as cursors — you run into the same problem. You can encounter content repetition. Let’s assume we have a high traffic forum with a very active community. Let’s look at a subset of such forums posts table:&lt;br&gt;
| ID  | Title   | ….  | LastActiveDate      |&lt;br&gt;
| --- | ------- | --- | ------------------- |&lt;br&gt;
| 675 | Title 1 |  …   | 2022-01-10T12:31:22 |&lt;br&gt;
| 123 | Title 2 |   …  | 2022-01-10T12:30:48 |&lt;br&gt;
| 534 | Title 3 |    … | 2022-01-10T12:30:23 |&lt;br&gt;
| 301 | Title 4 |     …| 2022-01-10T12:30:23 |&lt;br&gt;
| 231 | Title 5 |     …| 2022-01-10T12:30:23 |&lt;br&gt;
| 945 | Title 6 |     …| 2022-01-10T12:30:17 |&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: as earlier, the data is sorted by &lt;code&gt;LastActiveDate&lt;/code&gt; for demonstration purposes only.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For the sake of example, let’s say our page size is 2. And since we are looking at a subset of data, let’s say we hold a &lt;code&gt;nextPageCursor&lt;/code&gt; of &lt;code&gt;2022-01-10T12:31:22&lt;/code&gt;. What will be the result of the invocation of &lt;code&gt;getPosts("2022-01-10T12:31:22");&lt;/code&gt;? If you’ve read through the article and understood it well, you would be correct to answer: posts 123 and 534. And what will be the &lt;code&gt;nextPageCursor&lt;/code&gt; value for that invocation? The correct answer is &lt;code&gt;2022-01-10T12:30:23&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now, here’s the problem. Let’s say you want to navigate to the next page using &lt;code&gt;nextPageCursor = '2022-01-10T12:30:23'&lt;/code&gt;. What would be the result? That’s true — post ID 945. Since the query we’ve written earlier, translates to:&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;posts&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;LastActiveDate&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;LastActiveDate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'2022-01-10T12:30:23'&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are skipping posts ID 301 and ID 231. The hasty among you might just run and replace &lt;code&gt;&amp;lt;&lt;/code&gt; to &lt;code&gt;&amp;lt;=&lt;/code&gt;, but this introduces another issue. Now, instead of getting post ID 945 you get posts ID 534 and ID 301. So not only you are getting post ID 534 &lt;strong&gt;again&lt;/strong&gt;, your &lt;code&gt;nextPageCursor&lt;/code&gt; is set to &lt;code&gt;2022-01-10T12:30:23&lt;/code&gt; so you're essentially stuck in an endless loop (unless someone decides to update one of the posts).&lt;/p&gt;

&lt;p&gt;Dates, although provide chronological order, &lt;strong&gt;are not&lt;/strong&gt; providers of uniqueness. In a system with high rate of content, you might end up with collisions. And I know what you are thinking about: &lt;em&gt;I’ll just add milliseconds!&lt;/em&gt; While milliseconds &lt;em&gt;might&lt;/em&gt; help you to prevent this issue in human generated content (although there is still a chance of collision, depending on the traffic and the nature of the system) — they are not a solution for machine generated content in systems with high rate of content generation. There are other solutions, like content versioning, to provide uniqueness of the cursor.&lt;/p&gt;

&lt;p&gt;Also keep in mind that cursors do not have to be bound to one specific dimension. I once designed a system where the cursor was a JSON that held multiple vectors for data retrieval. This JSON was then &lt;code&gt;base64&lt;/code&gt; encoded and was used as a cursor for navigation. So you could combine multiple fields, and any other data you would want, inside the cursor. As long as you can pass the cursor in the URL and your system can decode it — it’s a viable solution.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why you should adopt Makefile in all of your projects</title>
      <dc:creator>Dmitry Kudryavtsev</dc:creator>
      <pubDate>Mon, 19 Dec 2022 09:48:28 +0000</pubDate>
      <link>https://dev.to/skwee357/why-you-should-adopt-makefile-in-all-of-your-projects-2l3i</link>
      <guid>https://dev.to/skwee357/why-you-should-adopt-makefile-in-all-of-your-projects-2l3i</guid>
      <description>&lt;p&gt;GNU Make. A software that is, most likely, older than you. It’s so simple, so standard, and so ignored. I’m here to provide a case in favor of make and Makefiles.&lt;br&gt;
What is make and Makefile?&lt;/p&gt;

&lt;p&gt;Before I even start to build my case, I need to explain to you what is make and Makefile.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is make and Makefile?
&lt;/h2&gt;

&lt;p&gt;make - is a build automation tool. It is written in C and was first released in April 1976. On Linux, make is usually available as part of the build tools dependencies that includes stuff like gcc - the C compiler etc. On Ubuntu, it's under build-essentials; on Arch Linux, under base-devel. MacOS users can install it with brew. And on Windows... I honestly have to clue what is going-on on Windows.&lt;/p&gt;

&lt;p&gt;But make does not work by itself. It needs instructions. And those instructions are stored in a Makefile.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I believe in owning my content, in the same way I own my computer or my car. Therefore, if you want to continue reading this article, for free, you can follow &lt;a href="https://www.yieldcode.blog/post/why-you-should-adpot-makefile-in-all-of-your-projects" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to my personal blog.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>watercooler</category>
      <category>design</category>
    </item>
    <item>
      <title>4 Ways to Minimize Your Dependencies in Node.js</title>
      <dc:creator>Dmitry Kudryavtsev</dc:creator>
      <pubDate>Wed, 14 Sep 2022 11:29:46 +0000</pubDate>
      <link>https://dev.to/appsignal/4-ways-to-minimize-your-dependencies-in-nodejs-485f</link>
      <guid>https://dev.to/appsignal/4-ways-to-minimize-your-dependencies-in-nodejs-485f</guid>
      <description>&lt;p&gt;We all know the joke about how &lt;code&gt;node_modules&lt;/code&gt; is the heaviest object in the universe.&lt;/p&gt;

&lt;p&gt;For example, a project that uses only &lt;code&gt;fastify&lt;/code&gt;, &lt;code&gt;knex&lt;/code&gt;, &lt;code&gt;typescript&lt;/code&gt;, and &lt;code&gt;uuid&lt;/code&gt; generates an 83MB &lt;code&gt;node_modules&lt;/code&gt; folder! That's huge! And those four packages are far from a complete set for a relatively small back-end. A more realistic size for &lt;code&gt;node_modules&lt;/code&gt; is north of 100MB, in some cases reaching 1GB.&lt;/p&gt;

&lt;p&gt;In this post, we'll explore four methods to minimize your code dependencies, resulting in faster CI/CD execution and safer code.&lt;/p&gt;

&lt;p&gt;But first, let's touch on some of the problems with heavy &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Issues with Heavy &lt;code&gt;node_modules&lt;/code&gt; in Node.js
&lt;/h2&gt;

&lt;p&gt;Heavy &lt;code&gt;node_modules&lt;/code&gt; can cause slower CI/CD pipelines, as dependencies are usually installed during those pipelines, and require network calls to a registry (whether &lt;code&gt;npm&lt;/code&gt; or your mirror of it). This affects your development experience.&lt;/p&gt;

&lt;p&gt;Moreover, package creep can introduce serious issues like security vulnerabilities, as you don't own the code that resides inside the packages.&lt;/p&gt;

&lt;p&gt;Let's jump into how to minimize the dependencies in your Node.js code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method One: Check the Age of Node.js Packages
&lt;/h2&gt;

&lt;p&gt;Imagine a situation where you have a function that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;findSomethingByIds&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function's purpose is to either find a single entity or a list of entities by their id. It's common practice with different repository patterns.&lt;/p&gt;

&lt;p&gt;However, let's say you want to know whether you've requested a single entity or a list of entities. A quick npm search leads us to &lt;a href="https://www.npmjs.com/package/isarray"&gt;npm/isarray&lt;/a&gt;, an insanely popular package with over 62 million weekly downloads! You know the drill:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i isarray
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Don't forget:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @types/isarray
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But wait! Before you do that, have you noticed that this package is &lt;strong&gt;three years old&lt;/strong&gt;? And if you do a better search, you will discover that &lt;code&gt;isArray&lt;/code&gt; is now part of the JavaScript core, can be invoked using &lt;code&gt;Array.isArray&lt;/code&gt;, and is perfectly supported in Node.js version 10 and up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray"&gt;Read more about &lt;code&gt;Array.isArray&lt;/code&gt; in Mozilla MDN&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Package age is a good first indicator. Many old (2+ years) packages might have security vulnerabilities or be outdated. Some of the outdated packages are also merged into the official JavaScript spec.&lt;/p&gt;

&lt;p&gt;Another example is the &lt;a href="https://www.npmjs.com/package/trim"&gt;trim&lt;/a&gt; package. It has more than 4 million downloads, and while it's not that old (only 1 year), a native solution exists in JavaScript core: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim"&gt;String.prototype.trim()&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So always try to find a native solution first, as JavaScript evolves quickly and its standard library is always expanding. Don't be fooled by the &lt;em&gt;weekly downloads&lt;/em&gt; counter in npm.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method Two: Using 'One-liner' Node.js Packages
&lt;/h2&gt;

&lt;p&gt;Many 'simple' packages are actually what I call 'one-liners'. A one-liner is a package that contains very few lines of code.&lt;/p&gt;

&lt;p&gt;If we continue with our &lt;code&gt;isArray&lt;/code&gt; example from above, by examining the content of &lt;code&gt;index.js&lt;/code&gt; we can see that it's a simple one-line function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;toString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isArray&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;[object Array]&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;&lt;em&gt;&lt;a href="https://github.com/juliangruber/isarray/blob/master/index.js"&gt;Source: GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I always recommend you look at the source code of packages, at least simple ones because they can teach you a lot. Looking at the above source code, we discover one important thing: that &lt;code&gt;Array.isArray&lt;/code&gt; exists (the same conclusion we came to with method one).&lt;/p&gt;

&lt;p&gt;However, even if we did not have a native is-array method, the entire function is a simple if statement. And instead of requiring this package as a dependency, we can simply write the code as part of our project. It's worth stopping for a minute and discussing the pros and cons of such an approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Npm vs. Writing Your Own Code
&lt;/h3&gt;

&lt;p&gt;Npm is a public registry, and the code contributed to npm comes from people worldwide. It is amazing that we have such a big repository of free and open-source code. However, this also comes with some disadvantages.&lt;/p&gt;

&lt;p&gt;Npm, unfortunately, is known to be at high risk of attack. Packages might get compromised, whether by third parties or by developers themselves.&lt;/p&gt;

&lt;p&gt;We all remember the scandal around &lt;a href="https://qz.com/646467/how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code/"&gt;&lt;code&gt;left-pad&lt;/code&gt;&lt;/a&gt; and the recent rise in crypto-mining and crypto-stealing code that resides inside popular npm packages.&lt;/p&gt;

&lt;p&gt;While it's impossible to audit every single package we install, we can lower the attack vector by using fewer packages in our projects. A great way to reduce the number of packages we depend on is to write trivial packages ourselves.&lt;/p&gt;

&lt;p&gt;On the other hand, by writing some code ourselves, we deprive ourselves of the community's sheer knowledge. Multiple people maintain popular packages, so they can quickly react to new vulnerabilities (for example, by monitoring the GitHub issues page of their package). This is something you, as a sole developer or part of a small organization, might lack the resources to do.&lt;/p&gt;

&lt;p&gt;So the next time you are eager to install a package, check its source code. It might be a one-liner that you can write instead of introducing a potential attack risk and slowing your CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;But don't go too far with this method: you don't want to reinvent the wheel or entirely deprive yourself of the community's support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method Three: Extract Sub-packages with &lt;code&gt;lodash&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Imagine we have the following object interface:&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SomeObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&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;p&gt;And we have a function that accepts an object with that interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getBazOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SomeObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you've guessed from the function's name, it will give us the value of &lt;code&gt;baz&lt;/code&gt;, or &lt;code&gt;defaultValue&lt;/code&gt;, if the path to &lt;code&gt;baz&lt;/code&gt; is &lt;code&gt;undefined&lt;/code&gt;. Here is one implementation of that function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getBazOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SomeObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;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;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ugly, right? It will get uglier if you need to deal with arrays. Luckily, we can use the popular &lt;a href="https://www.npmjs.com/package/lodash"&gt;&lt;code&gt;lodash&lt;/code&gt;&lt;/a&gt; library!&lt;/p&gt;

&lt;p&gt;Install &lt;code&gt;lodash&lt;/code&gt;, and the code becomes nice and easy to read:&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;import&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getBazOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SomeObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo.bar.baz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&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;Neat!&lt;/p&gt;

&lt;p&gt;However, if we install &lt;code&gt;lodash&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i lodash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And its type definitions (because we use TypeScript):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @types/lodash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We will introduce 8.4MB of dependencies to our &lt;code&gt;node_modules&lt;/code&gt;. That's a lot!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ &lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; node_modules
8.4M    node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have some experience now, so let's put it to practice!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package age&lt;/strong&gt; - &lt;code&gt;lodash&lt;/code&gt; is relatively old — it was published a year ago. I couldn't find any JavaScript core functionality to get a nested value from an object by a string key. Let's move on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-liner package&lt;/strong&gt; - &lt;code&gt;lodash&lt;/code&gt; is not a one-liner. It has tens of files and lots of tests. Even looking at the functionality of &lt;a href="https://github.com/lodash/lodash/blob/master/get.js"&gt;&lt;code&gt;_.get&lt;/code&gt;&lt;/a&gt;, it's not exactly a one-liner. While it's a simple two-line function, it has an internal dependency on &lt;code&gt;./internals/baseGet.js&lt;/code&gt;, which in turn depends on &lt;code&gt;./castPath.js&lt;/code&gt; and &lt;code&gt;./toKey.js&lt;/code&gt; — and each depends on more files! It's too much to be a candidate for extracting standalone code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So it seems we are stuck with &lt;code&gt;lodash&lt;/code&gt; then. But wait! There is another trick I want to show you! &lt;strong&gt;Sub-package extraction&lt;/strong&gt;. If we read the &lt;code&gt;lodash&lt;/code&gt; readme carefully, we will notice that every single functionality &lt;code&gt;lodash&lt;/code&gt; provides is extracted into its own &lt;a href="https://www.npmjs.com/search?q=keywords:lodash-modularized"&gt;sub-package&lt;/a&gt;, including the &lt;code&gt;_.get&lt;/code&gt; function!&lt;/p&gt;

&lt;p&gt;Instead of installing the entire &lt;code&gt;lodash&lt;/code&gt; library, we can install &lt;code&gt;lodash.get&lt;/code&gt; for just the &lt;code&gt;_.get&lt;/code&gt; function (of course, don't forget the type definitions). This reduces the &lt;code&gt;node_modules&lt;/code&gt; size from 8.4MB to 3.6MB. It's still a large folder, but a &lt;strong&gt;57%&lt;/strong&gt; reduction from the original size! I'll happily take such a percentage reduction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;❯ npm i lodash.get
❯ npm i &lt;span class="nt"&gt;-D&lt;/span&gt; @types/lodash.get
❯ &lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-sh&lt;/span&gt; node_modules
3.6M    node_modules
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;getBazOrDefault&lt;/code&gt; code will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;lodash.get&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getBazOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SomeObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;foo.bar.baz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;defaultValue&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In conclusion, be aware that many packages, especially collections of utilities like &lt;code&gt;lodash&lt;/code&gt;, can be published to &lt;code&gt;npm&lt;/code&gt; as individual packages. Instead of installing the entire 312 different methods from &lt;code&gt;lodash&lt;/code&gt;, we can install the ones we actually need, reducing the weight of our &lt;code&gt;node_modules&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Method Four: Do It Yourself
&lt;/h2&gt;

&lt;p&gt;Last but not least is the DIY method.&lt;/p&gt;

&lt;p&gt;Let's say we need a method to capitalize the first letter of each word in a given string. We can run an npm search for the term &lt;a href="https://www.npmjs.com/search?q=capitalize"&gt;capitalize&lt;/a&gt; and be presented with 266 different packages (not all relevant).&lt;/p&gt;

&lt;p&gt;We can spend some time looking for a relevant, relatively maintained package and add it as a dependency to our project. Or we can write it ourselves!&lt;/p&gt;

&lt;p&gt;Why, you ask? Software engineering is the art of problem-solving. To become a great software engineer, you need to be able to solve software engineering problems.&lt;/p&gt;

&lt;p&gt;If you only know how to use third-party packages and copy code from Stack Overflow, you will be a software engineer, but not a great one. And you do want to be great, don't you? Then let's write our own capitalize function!&lt;/p&gt;

&lt;p&gt;Let's start with a signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, let's identify words from our string. We can do that by splitting the string with whitespace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we need to capitalize the first letter of each word:&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;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And lastly, we need to join the words back into a string:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;capitalizedString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the entire function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;charAt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you run this with some example strings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// Hello World&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;capitalize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how are you?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// How Are You?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Great success!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; - this function is for demonstration purposes only. It does not consider scenarios like quoted words — for example, &lt;code&gt;hello "world"&lt;/code&gt; will be capitalized incorrectly. However, this is a great opportunity to up your problem-solving skills! Go on and figure out how to capitalize quoted words as well!&lt;/p&gt;

&lt;p&gt;As with the second method, you'll want to avoid 'reinventing the wheel' here. On the one hand, you don't want to write the same functionality over and over. On the other hand, you don't want to introduce a dependency for every small functionality you need.&lt;/p&gt;

&lt;p&gt;Always analyze and evaluate each solution. Many packages do way more than you need, so it's better to implement the code yourself. However, there are good quality packages for common utils like &lt;a href="https://www.npmjs.com/package/slugify"&gt;slugify&lt;/a&gt;, which is well-maintained and provides basic functionality you probably wouldn't want to implement yourself in a production environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrap Up
&lt;/h2&gt;

&lt;p&gt;In this post, we ran through four methods to analyze packages and shrink your &lt;code&gt;node_modules&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checking the age of packages&lt;/li&gt;
&lt;li&gt;Using one-liner packages&lt;/li&gt;
&lt;li&gt;Extracting sub-packages&lt;/li&gt;
&lt;li&gt;Doing it yourself&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These methods should make your development process faster, reducing the number of packages you need to pull every time you run your CI/CD pipeline.&lt;/p&gt;

&lt;p&gt;All in all, the &lt;code&gt;npm&lt;/code&gt; ecosystem is great! It is the biggest package registry as of today. But installing packages shouldn't be a panacea. Our project is not only the code we write — it's also the packages it consists of. Knowing what goes into our &lt;code&gt;node_modules&lt;/code&gt; makes our projects more resilient and makes us better developers.&lt;/p&gt;

&lt;p&gt;Until next time, happy coding!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S. If you liked this post, &lt;a href="https://blog.appsignal.com/javascript-sorcery"&gt;subscribe to our JavaScript Sorcery list&lt;/a&gt; for a monthly deep dive into more magical JavaScript tips and tricks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.P.S. If you need an APM for your Node.js app, go and &lt;a href="https://www.appsignal.com/nodejs"&gt;check out the AppSignal APM for Node.js&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>node</category>
    </item>
  </channel>
</rss>
