Một pull request sửa package.json nhưng quên cập nhật lockfile. Máy developer vẫn chạy được, còn CI dừng ngay ở bước cài dependency. Trước khi thay lệnh CI để pipeline xanh trở lại, bạn cần biết sự khác nhau đó đang bảo vệ điều gì.
Bài này đánh giá hai lệnh theo hành vi cài đặt, không đưa ra benchmark tốc độ. Fixture offline được chạy với Node.js 24.18.1 và npm 11.16.0. Nguồn là tài liệu nhánh npm v11, đọc ngày 24/10/2026; đó không phải cam kết mọi phiên bản npm có output giống hệt.
1. Hai lệnh phục vụ hai thời điểm khác nhau
Theo tài liệu npm ci, lệnh cần lockfile có sẵn, từ chối khi dependency trong manifest không khớp lockfile, và cài toàn bộ dự án. Nó thay thế node_modules đang có và không ghi lại manifest hay lockfile.
Theo tài liệu npm install, install có thể dùng phiên bản trong lockfile khi chúng phù hợp yêu cầu. Khi yêu cầu thay đổi, nó có thể giải quyết lại và cập nhật lockfile. Vì vậy, nói install “luôn lấy bản mới nhất” là mô tả thiếu chính xác.
Lockfile cần được review và đưa vào Git. Tài liệu package-lock.json mô tả vai trò ghi lại cây dependency. Việc có file này không thay thế kiểm thử ứng dụng, kiểm tra lifecycle script hay lựa chọn môi trường build.
2. Thử một tình huống lệch lockfile không cần registry
Tạo một thư mục thử mới, không dùng dự án đang làm. Các file bên dưới không có script cài đặt và không tải package bên ngoài.
mkdir npm-lock-demo
cd npm-lock-demo
printf '%s\n' '{"name":"npm-lock-demo","version":"1.0.0","private":true}' > package.json
npm install --package-lock-only --offline --ignore-scripts --no-audit --no-fund
npm ci --offline --ignore-scripts --no-audit --no-fund
mkdir fixture-package
printf '%s\n' '{"name":"fixture-package","version":"1.0.0"}' > fixture-package/package.json
node -e 'const fs=require("node:fs");const p=JSON.parse(fs.readFileSync("package.json","utf8"));p.dependencies={"fixture-package":"file:./fixture-package"};fs.writeFileSync("package.json",JSON.stringify(p,null,2)+"\n")'
npm ci --offline --ignore-scripts --no-audit --no-fund
Lệnh ci cuối được kỳ vọng thất bại: dependency local đã có trong manifest nhưng chưa có trong lockfile. Đó là lỗi cố ý của bài học. Trên bộ phiên bản đã chạy, ci báo thiếu package trong lockfile và không đổi hash lockfile.
Sau khi xem lỗi, giải quyết sự không khớp bằng một lần install có chủ đích, rồi kiểm tra ci lại:
npm install --offline --ignore-scripts --no-audit --no-fund
npm ci --offline --ignore-scripts --no-audit --no-fund
Ở fixture này, install cập nhật lockfile và ci sau đó thành công. Dependency file: chỉ để tạo ví dụ tự đủ; bài không suy ra hành vi hiệu năng của registry, workspace phức tạp hay package native từ thử nghiệm này. Các cờ tắt script, audit và fund phục vụ phạm vi bài thử, không phải cấu hình production mặc định cho mọi dự án.
3. So sánh theo cùng tiêu chí
Về độ dễ bắt đầu, install thuận tiện khi tạo dự án và thêm dependency. Ci cần bộ manifest cùng lockfile đã thống nhất, nên phù hợp lúc nhận một trạng thái dự án đã được commit để kiểm tra.
Về quyền kiểm soát, install cho phép cập nhật dependency ở bước phát triển; diff lockfile là đầu ra cần review. Ci đặt một điểm chặn: pipeline không tự chữa manifest lệch bằng cách viết lockfile mới. Nếu lỗi xuất hiện trong CI, người sửa dependency cần hoàn thiện thay đổi trong pull request.
Về hệ sinh thái, cả hai nằm trong npm và dùng cùng dự án Node. Nhưng phiên bản npm, hệ điều hành, dependency tùy chọn và cấu hình giải quyết cây vẫn cần được quản lý. Tài liệu ci lưu ý các cờ ảnh hưởng cây như legacy-peer-deps hoặc install-links phải nhất quán với lúc tạo lockfile. Nếu nhóm cần một cấu hình, ghi nó ở mức dự án theo chính sách của nhóm thay vì mỗi người tự nhớ.
Về hiệu năng, bài này không có số đo để xếp hạng. Muốn so sánh thời gian CI, hãy cố định runner, phiên bản công cụ, tình trạng cache và package tree. Đo nhiều lần và giữ log; không dùng fixture rỗng ở trên để kết luận lệnh nào nhanh hơn trong repository thật.
Về chi phí vận hành, ci làm lỗi dependency lộ sớm nhưng nhóm phải xử lý chúng đúng nơi. Install thuận tiện cho thay đổi có chủ đích nhưng cần người review diff. Cả hai đều không tự chứng minh dependency an toàn hoặc ứng dụng còn hoạt động.
Kiểm tra trong repository thật
Ở máy phát triển, thay dependency bằng install, review package.json và lockfile, rồi chạy kiểm thử liên quan. Trước khi gửi pull request, thử ci trong checkout sạch. Ở CI, chọn phiên bản Node/npm rõ ràng, chạy ci rồi đến build/test.
Nếu ci lỗi, kiểm tra manifest và lockfile có cùng commit không, lockfile có được bỏ qua trong checkout không, và cấu hình tạo cây có giống local không. Đừng xóa lockfile chỉ để dập lỗi; bạn sẽ mất một phần thông tin cần điều tra. Cũng đừng coi --ignore-scripts là cách sửa mọi package lỗi build: một số dependency cần script để hoạt động.
Kết luận
Dùng install cho bước thay đổi dependency có review và dùng ci để kiểm tra một trạng thái dependency đã thống nhất là một lựa chọn rõ ràng cho nhiều nhóm. Quyết định nên dựa trên quy trình lockfile và bằng chứng build của dự án, không chỉ tên lệnh hay lời hứa tốc độ.




