The Package Manager

Fredd Somm

Fredd Somm

· 10 min read
A person holding a logo of npm.

This article will first explain the importance of dependencies and package managers and then discuss common solutions in the JavaScript/TypeScript ecosystem to handle dependencies.

What is a dependency, and why does it matter?

In software development, dependencies refer to an external piece of code, library, module, or package that a software project relies on to function correctly. External means you import external code in your current project, as it may seem that it does not make sense to code it yourself, is cumbersome or already works perfectly in other open-source projects.

Engineers commonly share large amounts of code—especially within the JavaScript/TypeScript community—to avoid rewriting essential components (e.g., a “like” button), libraries, or frameworks. However, that means the entire ecosystem becomes more dependent on each other. For example, I depend on the open-source component codemirror to display code text snippets for IndieHacker.Info.

It's critical to realize that practically all software is constructed on top of other software, which depends on other software. They are all dependent on each other, and if one heavily used project has bugs, it can cause massive difficulties in debugging or even widespread disruptions, as the left-pad incident has shown (click here for more).

Hence, I am not the biggest fan of dependencies. Especially in terms of compatibility issues. Whenever there is an update or a dependency change, it often takes me quite some time to debug it, which becomes tiresome. I have to read the error message, view what component breaks if it has other dependencies and what upgrade or downgrade would fix the issue. Removing and re-installing the runtime (like Node.js) often solves the issue.

Why do dependencies matter?

In my opinion, dependencies are the lesser evil to facilitate three things:

  1. External code makes developers more efficient and productive: I can quickly extend IndieHacker.Info's capabilities by providing pre-built solutions for everyday tasks or functionalities, such as code text snippets, without building and testing them from scratch.
  2. External code makes code more modular and maintainable: Using dependencies facilitates modular design by breaking down complex systems into smaller, manageable components. This modular approach makes the codebase easier to understand, maintain, and update. These dependencies also make it harder to maintain due to their cross-dependencies. However, finding an outstanding balance between not having too many dependencies and being efficient is the high art of software engineering.
  3. Integrations, Integrations, Integrations: Dependencies allow projects to integrate seamlessly with external systems, services, or platforms. For example, for IndieHacker.Info, I rely on Sanity(a CMS), NextJs, JavaScript, Typescript, and a couple of integrations I have built in between. Hence, dependencies are unavoidable in most cases.

Hence, using dependencies can make development much more time-efficient. However, it also comes with maintenance costs, as more code equals more dependencies, which in turn equals more code paths. Every code dependency risks a bug, and any defect might lead to a vulnerability. So, it is often advised to keep the codebase simpler (K.I.S.S. = Keep It Simple Stupid). But how can you facilitate effective dependency management essential for building robust, maintainable, scalable software applications? This is where a package manager comes into play.

What is a package manager?

Before the advent of package managers, JavaScript engineers typically relied on a handful of dependencies stored directly within their projects or served via content delivery networks (CDNs). Handling an ever-increasing number of code package dependencies was a considerable pain, which is why package managers were born.

A package manager (i.e., a package-management system) is a software tool that systematically automates the steps involved in installing, updating, customizing, and uninstalling computer programs, such as software packages. Packages contain metadata (usually the package.json file in most JavaScript/TypeScript applications), which provides an essential description of the program's name, an explanation of its function, the version number, the vendor, and a list of dependencies the program needs to operate. Hence, package.json is an extremely informational tool, and whenever you look at an existing project, I would always recommend reading it first to understand the scope of the project.

The purpose of a package manager is to eliminate the need for manual updates and installations, which is indispensable for most software applications and Indie Hackers.

Popular Package Managers

There are three popular package managers you will likely encounter frequently:

Popular Package Managers:

  1. npm (Node Package Manager): npm is the default package manager for Node.js, a popular runtime environment for JavaScript. It hosts the largest ecosystem of open-source JavaScript packages (around 800’000 code packages) and is widely used for building web applications, server-side applications, and command-line tools. I often use it, as it is the default and very commonly used. Especially when you read on https://stackoverflow.com/ instructions on handling your bug, it usually gives command line instructions with npm and not with other package managers, so you do not have to do the mental gymnastics of adjusting your command line.
  2. Yarn: Yarn is a fast, reliable, secure JavaScript package manager developed by Meta as an alternative to npm. It is known for its speed and offline mode. I used to work with Yarn quite often, but I encountered certain hiccups with it at times, so I do not use it in my projects. Sometimes, you may find yourself inadvertently mixing Yarn with npm, resulting in the creation of yarn.lock files. This can lead to complications when deploying to production environments.
  3. PNPM (Pinned Node Package Manager): PNPM is a lightweight and efficient package manager for Node.js applications. It optimizes disk space and improves installation speed using a unique file-linking approach and a content-addressable store. I use pnpm quite often nowadays. Also for IndieHacker.Info.

In the end, it does not matter which package manager you use. I recommend trying all three in your projects to see which one you like best.

How to install a Package Manager to your project

To install a package manager on a new project, follow these steps by using the terminal:

Step 1: First, open your desired folder to create a new ex. Next.js project. You can navigate to the folder.

cd ../Desktop/yourCodeFolder

Step 2:

If you have never downloaded Node.js (a runtime environment), you should first do so. You can download and install Node.js from https://nodejs.org/. npm is installed by default with Node.js. Verify the installation and its version by entering node —v into your terminal.

Step 3:

Use either 1. npm, 2. yarn or 3. pnpm for your new project.

npm -v //1. npm is already installed automatically with Node.js. Check maybe the version

#or

npm install --global yarn //2. Yarn can be installed ironically through npm

#or

npm install -g @pnpm/exe //3. pnpm can also be installed through npm

Step 4: You're ready to create a new project. This example uses Next.Js:

# Create a new Next.js project using npm
npx create-next-app@latest my-next-app

# Create a new Next.js project using Yarn
yarn create next-app my-next-app

# Create a new Next.js project using pnpm
pnpx create-next-app my-next-app


2 things to know:

1. .gitignore

A .gitignore file specifies intentionally untracked files that Git should ignore, such as sensitive information or unnecessary code, such as node.js, as any browser has its own runtime environment and does not need node.js. Some of your files listed in .gitignore contain sensitive information (like API keys), so they should not be tracked in your GitHub or any other version control, as it can cause vulnerability issues (if your GitHub data or API keys are leaked) and might cost a lot of money. Thus, do not touch the automatically generated .gitignore file.

2. Mixing package managers in one project

Mixing different package managers like npm, Yarn, and pnpm in one project is not advisable due to potential inconsistencies in dependency resolution and build reproducibility. Each package manager generates its own lock file (e.g., package-lock.json for npm, yarn.lock for Yarn, pnpm-lock.yaml for pnpm) to ensure consistent dependency versions across environments. Mixing package managers can lead to conflicts in lock file formats and undermine the purpose of lock files, compromising build reproducibility and introducing potential issues during development, testing, and production.

Conclusion:

Dependencies and package managers are vital for modern software development, especially in the JavaScript/TypeScript realm. They enable efficiency, code reuse, and integrations with external systems. While managing dependencies can be challenging, package managers like npm, Yarn, and pnpm streamline the process.

I hope this article helps you to choose a package manager you prefer and gives you more confidence in building your own Indie Hacking Projects. I would love to hear your from your projects and how I can help you with IndieHacker.Info.

Fredd Somm

About Fredd Somm

I deeply care about creating exceptional digital experiences but come from a non-technical background.

IndieHacker.Info is the guide I wished I had to start my own SaaS company as a total beginner. Pre-order the Notion Template "From 0 to IndieHacker" now!

Made with ❤️ by FreddSomm.com
Copyright © 2024 IndieHacker.Info | Subscribe to my newsletter here