Skip to content
Muhammet Şafak
tr
Tools & Technologies 3 min read

npm and the Modern JavaScript Package Ecosystem

Managing JavaScript dependencies with npm, configuring package.json correctly, and handling the practical challenges you run into in the package ecosystem.


After adopting Composer in the PHP world, I remember thinking “every language should have something like this.” On the JavaScript side, npm (Node Package Manager) has been filling that role for years. Working on Vue and frontend projects for a while now has deepened my relationship with npm considerably. In this post I’m collecting what I’ve learned through daily practice.

What is npm?

npm is a package manager that lets you install, share, and manage JavaScript packages. It ships with Node.js and also serves as your gateway to the world’s largest software package registry.

The package.json file defines a project’s identity and its dependencies. Think of it as the JavaScript equivalent of Composer’s composer.json:

{
  "name": "my-project",
  "version": "1.0.0",
  "scripts": {
    "dev": "webpack --mode development",
    "build": "webpack --mode production"
  },
  "dependencies": {
    "axios": "^0.18.0",
    "vue": "^2.5.0"
  },
  "devDependencies": {
    "webpack": "^4.0.0",
    "vue-loader": "^15.0.0"
  }
}

The dependencies vs devDependencies distinction

It’s easy to mix these two up. The rule is simple: packages that need to run in production go into dependencies; packages used only during development go into devDependencies.

  • vue, axiosdependencies (needed at runtime)
  • webpack, babel, eslintdevDependencies (build/development tools)
# Add as a production dependency
npm install axios

# Add as a development dependency
npm install --save-dev webpack

Why does it matter? When you run npm install --production on a server, only dependencies are installed — devDependencies are skipped. This keeps your production environment from being bloated with packages it doesn’t need.

The package-lock.json file

After installing packages, npm generates a file called package-lock.json. This file locks the exact versions of every installed package and all their transitive dependencies — the same purpose served by composer.lock in the Composer world.

Committing this file to Git is important. To avoid “it worked on my machine” problems, everyone on the team (and your servers) needs to install the exact same versions. package-lock.json is what guarantees that.

To install using the locked versions:

npm ci

Using npm ci instead of npm install enforces strict adherence to package-lock.json and rebuilds node_modules from scratch. It’s ideal for CI environments.

Version ranges

The version specifiers inside package.json can be confusing at first:

  • "axios": "0.18.0" — install exactly this version
  • "axios": "^0.18.0" — 0.18.0 or higher, but below 1.0.0 (allows minor/patch updates)
  • "axios": "~0.18.0" — 0.18.0 or higher, but below 0.19.0 (patch updates only)

Most projects use ^, which means “open to compatible updates.” Because package-lock.json stores the exact installed version, that flexibility stays under control when you use npm ci.

npm scripts

The scripts section of package.json is a heavily used feature. You can define short aliases for build commands, test runners, and other repetitive tasks:

{
  "scripts": {
    "dev": "webpack --watch --mode development",
    "build": "webpack --mode production",
    "lint": "eslint src/"
  }
}

Once defined, running them is straightforward:

npm run dev
npm run build
npm run lint

This means someone new to the project doesn’t have to learn which tool does what and how to invoke it — package.json acts as a kind of project guide.

node_modules and .gitignore

The node_modules directory can get large — sometimes hundreds of megabytes. It should never be committed to Git. Add this line to your .gitignore:

node_modules/

That’s all you need. Running npm install will read package.json and package-lock.json and download everything required.

The Yarn alternative

As of 2018, Yarn has become a serious alternative to npm. Developed by Facebook, Yarn offers some advantages over npm — particularly around performance and reliability. Because it’s fully compatible with the package.json format, the two tools can be used interchangeably on the same project without friction. Which one you choose usually comes down to team preference or habit.

Conclusion

Using npm properly is the fundamental step toward avoiding dependency chaos in JavaScript projects. Respecting the dependencies/devDependencies split, committing package-lock.json to your repository, and standardizing your workflow commands with npm scripts — these are small habits, but they’re critical for maintaining a tidy project structure.

Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

Related Posts

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind