Optimizing CI/CD Pipelines: Why You Should Use npm ci Instead of npm install

Introduction
In modern software development, efficient and reliable Continuous Integration/Continuous Deployment (CI/CD) pipelines are crucial. One common but often overlooked aspect is the use of npm install vs. npm ci in Node.js-based projects, particularly within CI/CD processes. This article explains why npm ci is typically a better choice than npm install in CI/CD contexts, with an example using GitLab CI.
Understanding npm install and npm ci
npm install:
- It is the standard command to install dependencies from the
package.jsonfile. - It updates the
package-lock.jsonfile if it finds discrepancies withpackage.json. - While ensuring that packages meet the version requirements specified, it might install newer versions of dependencies if allowed by the specified version ranges, potentially leading to different versions being installed in different environments.
npm ci(Continuous Integration):
- Introduced in npm 5.7.0, it’s specifically tailored for CI/CD pipelines.
- It installs dependencies exactly as specified in
package-lock.json, ensuring consistent dependencies across all environments. - It’s generally faster than
npm installas it skips certain user-oriented features and focuses purely on installing packages at exact versions.
Why Prefer npm ci in CI/CD Pipelines
- Consistent Dependencies: Using
npm ciensures that the exact versions of the dependencies are installed as per thepackage-lock.jsonfile, reducing the chances of introducing bugs due to minor differences in dependencies. - Faster Builds:
npm ciis faster thannpm installas it bypasses certain checks and computations done by the latter. This speed-up is beneficial in CI/CD where build time is critical. - Clean Slate:
npm cistarts by deleting thenode_modulesdirectory, providing a clean slate. This ensures that no outdated or irrelevant packages are part of the build, which could happen withnpm install.
Example: Using npm ci in GitLab CI
Here’s how to use npm ci in a .gitlab-ci.yml file for a Node.js project:
image: node:latest
cache:
paths:
- node_modules/
stages:
- install
- build
- test
install_dependencies:
stage: install
script:
- npm ci
artifacts:
paths:
- node_modules/
build_project:
stage: build
script:
- npm run build
run_tests:
stage: test
script:
- npm run testIn this GitLab CI example:
- Image Specification: We use the latest Node.js image.
- Caching: The
node_modulesdirectory is cached to speed up subsequent runs. - Stages: Defined as install, build, and test.
- Install Dependencies: We use
npm cito install dependencies, ensuring consistency and speed. - Build and Test: Subsequent stages for building and testing the application.
Best Practices and Considerations
- Always Commit
package-lock.json: Ensure thatpackage-lock.jsonis always committed to your repository fornpm cito function correctly. - Regularly Update Dependencies: Regularly update your dependencies to ensure you get the latest security patches and improvements. This can be done in a controlled manner on a development branch using
npm install. - Understand CI/CD Requirements: While
npm ciis generally preferable in CI/CD, there may be cases wherenpm installis more suitable, such as when you intentionally want to test the latest versions of dependencies.
Conclusion
Replacing npm install with npm ci in your CI/CD pipelines, especially with platforms like GitLab CI, can lead to more consistent, reliable, and faster builds. This practice is an essential step in optimizing your CI/CD process for Node.js applications.
Next Steps
- Implement
npm ciin your existing CI/CD pipelines and observe the improvements. - Keep your
package-lock.jsonfile up-to-date with your current development state. - Continuously monitor and optimize your CI/CD processes for maximum efficiency and reliability.
In summary, embracing npm ci in CI/CD pipelines is a best practice for Node.js projects, ensuring stability and efficiency in the software development lifecycle.