如何導入版本管理與套件管理工具
很多時候我們都是裝了不同版本的語言,也有一些舊專案了,才接觸到版本套件管理的概念,這時就要在不破壞舊專案的前提下分階段導入。我會分三個階段逐步導入,分別是策略階段、執行階段與清理階段。
Richard Lin 林柏儒,2025-06-10

為什麼需要版本管理與套件管理

想像一下,在自己房間中找出一台舊 iPhone,想要充電時,卻發現新的充電線根本插不進舊 iPhone 的充電孔……這不代表充電線是錯的,畢竟新的 iPhone 要用新的充電線來充電。舊的充電線留著也很麻煩,或許早就丟了,但如果不留著舊的充電線,舊的 iPhone 要怎麼辦?

這個困境在開發時也會遇到。程式語言和開發套件會不斷更新版本,造成不同專案使用的版本不同。如果沒有妥善管理,就會發生各種版本衝突,就像新的充電線插不進舊 iPhone 的充電孔。

假如有個 iPhone 工具箱管理員,他準備好幾個工具箱,上面貼著 iPhone 5、iPhone X、iPhone 15 等不同標籤。標示 iPhone 5 的工具箱中,有全套的充電線、變壓器、保護貼、手機殼,全部相容於 iPhone 5。而另一個標示 iPhone 15 的工具箱,則裝滿了相容於 iPhone 15 的工具們,工具衝突的問題再也沒有發生。

回到開發的情境,如果有做好版本管理與套件管理,另一位開發者就可以根據我們提供的安裝步驟,按部就班的重現整個專案環境,並順利運行程式。

確保跨版本、跨時空、跨團隊的順暢協作,就是版本管理與套件管理的核心價值。

定義

在接下來的討論中,我的定義如下:

  • 版本管理:管理程式語言的版本
  • 套件管理:管理專案中的套件依賴

在本機開發時,搞定這兩者就大致夠用。

一旦需要部署其他伺服器,就需要像 docker 這種更大範圍的系統環境管理工具,就像一位工廠管理員,連工具箱管理員(版本管理工具)都是他請來的。這部份範圍更大,我們先不討論。

導入流程

如果還沒有任何專案,那太好了,直接安裝版本管理與套件管理系統即可。

然而,很多時候我們都是裝了不同版本的語言,也有一些舊專案了,才接觸到版本套件管理的概念,這時就要在不破壞舊專案的前提下分階段導入。

我會分三個階段逐步導入,分別是策略階段、執行階段與清理階段。

策略階段

  • 備份目前環境配置
    • 語言版本
    • 全域套件
  • 確認版本管理策略
    • 主要使用哪個版本
    • 支援哪些舊版
    • 移除哪些舊版
  • 確認套件管理策略
    • 少數套件全域安裝
    • 其他套件根據專案安裝
  • 確認專案導入策略
    • 分析不同專案導入的價值與難度
    • 排定導入的優先順序
    • 準備導入失敗時的 roll back 方案

執行階段

  • 選擇並安裝版本管理與套件管理工具
  • 備份專案
  • 按照策略執行導入,遇到錯誤時想辦法排除
  • 測試用新工具運行舊專案是否順利

清理階段

  • 保留一個版本獨立安裝,作為版本與套件管理工具失效時的備案
  • 測試這個備案版本是否成功運作
  • 逐步清除舊版程式語言,每次都測試相關的舊專案是否成功運作
  • (optional) 設定 PATH 指向新工具

接下來,我會分別以 Node.js、PHP、Python 為例,分享我如何實作。系統一律都是 macOS 15.4。

附上不同語言生態的工具對照:

功能類別Node.js 生態PHP 生態Python uv
版本管理nvmvaletuv
套件管理pnpmcomposeruv
專案設定檔package.jsoncomposer.jsonpyproject.toml
鎖定檔pnpm-lock.yamlcomposer.lockuv.lock

版本管理常用指令對照:

操作Node.js (nvm)PHP (valet)Python (uv)
安裝版本nvm install 18.19.0valet use php@8.2uv python install 3.12
切換版本nvm use 18.19.0valet use php@8.1uv python pin 3.11
列出版本nvm listuv python list

套件管理常用指令對照:

操作Node.js (pnpm)PHP (composer)Python (uv)
安裝依賴pnpm installcomposer installuv sync
新增套件pnpm add pkgcomposer require pkguv add pkg
移除套件pnpm remove pkgcomposer remove pkguv remove pkg
更新套件pnpm updatecomposer updateuv sync --upgrade


Node.js 版本套件管理實作

先從 Node.js 生態系開始談,因為相對單純。請不要直接到 Node.js 官網下載 Node.js,而是先從版本管理工具 nvm 開始。

版本管理工具:nvm

對於 Node.js 來說,工具箱管理員就是 nvm(Node Version Manager), 可以下載、管理不同版本的 Node.js(工具箱)。

最新的安裝與使用方式,請參考 nvm 官方 GitHub

安裝 nvm 完成後,透過 nvm install 指令安裝自己有用過的 Node.js 版本,在此之前我居然手動灌過三個 Node.js!建議先安裝 LTS 版作為預設版本,會是比較穩定的選項。

接著最重要的是在專案根目錄新增 .nvmrc 檔案,裡面寫好專案使用的 Node.js 版本(例如 22.16.0),nvm 會根據這個檔案讀取、切換專案的 Node.js 版本。

以下是 nvm 的實用指令:

  • nvm install node-version:下載並安裝指定版本的 Node.js(例如:nvm install 22 或 nvm install lts/*
  • nvm install --lts:安裝最新的 LTS(長期支援)版本
  • nvm uninstall node-version:移除指定版本的 Node.js
  • nvm current:顯示目前使用的 Node.js 版本
  • nvm use node-version:切換到指定版本的 Node.js(例如:nvm use 22
  • nvm use --lts:切換到最新的 LTS 版本
  • nvm ls:列出所有已安裝的 Node.js 版本
  • nvm ls-remote:列出所有可安裝的 Node.js 版本
  • nvm alias default node-version:設定預設的 Node.js 版本
  • nvm alias 別名 node-version:為指定版本建立別名

套件管理工具:npm

Node.js 在下載時就內建了套件管理工具 npm,有需要的話也有 yarn / pnpm / bun 等其他工具可以選擇。

npm 透過 package.json(工具清單)來管理開發套件(工具),會在下載套件之後放在 node_modules 資料夾,並用 package-lock.json 記錄這些套件的相關資訊。日後部署或建立 CI/CD pipeline 時,用 npm ci 指令就可以下載 package-lock.json 中記錄的套件。

最新的使用方式請參考 npm 官方文件

以下整理 npm 基本指令:

  • npm init:建立新專案的 package.json 檔案
  • npm install:安裝所有 package.json 中的相依套件
  • npm install package-name –save:安裝特定套件到 dependencies 並更新 package.json
  • npm install package-name --save-dev:安裝到 devDependencies 並更新 package.json
  • npm ci:使用 package-lock.json 快速安裝,用於 CI/CD
  • npm uninstall package-name:移除套件
  • npm prune:清理未使用的套件
  • npm ls:查看已安裝套件樹狀結構
  • npm run script-name:執行自定義 script
  • npm update:更新所有套件到允許範圍內的最新版本

到此為止,已經成功建立 Node.js 的版本與套件管理系統。

升級版套件管理工具:pnpm

如果不滿意 npm,在 yarn / pnpm / bun 這幾個 npm 的替代方案中,我會選擇 pnpm 作為 npm 的高效替代方案。因為大部分的指令只是多打一個 p 而已,學習難度低,可以讓 npm 的使用者無痛升級,享受高速下載與更少使用空間的優點。

最新的安裝與使用方式,請參考 pnpm 官方文件

這裡建議透過 Node.js 中「管理套件管理工具的工具」corepack 來下載,這樣只要先透過 nvm use 切換 Node.js 版本,在每個 Node.js 版本中都先輸入 corepack enable,只要透過 corepack enable pnpm 指令下載一次 pnpm,就可以在所有 Node.js 版本中通用。

反之如果是透過 npm 下載 pnpm,pnpm 就會成為特定 Node.js 版本下的依賴套件,只要切換 Node.js 版本就需要重新下載了。

整體來說會形成以下的層級結構:

系統層:macOS/Linux/Windows
├── nvm 管理層(Node.js 版本管理)
│   └── Node.js v20.11.1
│       ├── npm 10.8.0(內建 package manager)
│       └── corepack
│           └── pnpm@9.x.x(快取於此版本)
│
└── 專案層:專案級 package manager 版本控制
    ├── 專案 A:package.json 指定 "packageManager": "pnpm@9.x.x"
    └── 專案 B:使用預設 npm(無 packageManager 欄位)

pnpm 透過 package.json 來管理開發套件(工具),並用 pnpm-lock.yaml 記錄這些套件的相關資訊,完全忽略 package-lock.json。

相對於 npm 會在每個專案的 node_modules 資料夾中下載所有套件,pnpm 設計了全域儲存機制,可以把套件指向全域儲存的檔案,並避免重複下載,大幅節省了空間使用。

整體來說,pnpm 的指令和 npm 幾乎完全一樣,除了多打一個 p 以外,只有少數幾點不同:

  • npm install package-name 對應 pnpm add package-name
  • npm uninstall package-name 對應 pnpm remove package-name
  • npm run 對應 pnpm

PHP 版本套件管理實作

和 Node.js 不同,我們需要直接下載 PHP,在過程中會自動灌好套件管理工具 composer,之後再用 composer 全域下載 valet(macOS 限定)來管理不同專案中使用的 PHP 版本。

PHP 最新的安裝方式,請參考 PHP 官方文件

版本管理工具:valet

valet 是 Laravel 生態系中的環境管理工具,只能在 macOS 系統中使用,其中就有 PHP 版本管理的功能。基於我的 PHP 專案會以 Laravel 框架為主,所以選擇 valet 來管理 PHP 版本,可以獲得和 Node.js 的 nvm 幾乎一樣的體驗。

如果你需要和 nvm 完全對標的工具,可以考慮 phpbrew,雖然我安裝時一直遇到問題而放棄。

最新的安裝與使用方式,請參考 Laravel 官方文件,並在最新版本中尋找 valet 的文件

安裝之後,最重要的是在專案的根目錄中新增 .valetrc 檔案,寫好專案使用的 PHP 版本(例如 php=php@8.4),這樣 valet 就可以讀取這個檔案來切換 PHP 版本,使用體驗和 nvm 很像。

以下整理 valet 基礎管理指令:

  • valet install:安裝並配置 Valet 和 DnsMasq
  • valet uninstall:解除安裝 Valet,加上 --force 強制刪除所有資源
  • valet restart:重啟 Valet 服務
  • valet start:啟動 Valet 服務
  • valet stop:停止 Valet 服務
  • valet trust:允許 Valet 指令無需密碼執行

以及 PHP 版本管理的相關指令:

  • valet use php@version:切換 PHP 版本,如果沒有下載這個版本則會自動透過 Homebrew 下載
  • valet use php:使用當前專案配置的 PHP 版本執行 PHP
  • valet isolate php@version:為當前專案設定特定 PHP 版本
  • valet unisolate:移除專案的 PHP 版本隔離,回到全域版本
  • valet isolated:顯示所有已隔離的網站及其 PHP 版本

套件管理工具:composer

composer 就是 PHP 生態系中的工具箱,在下載 PHP 的過程中也會一併安裝。composer 透過 composer.json(工具清單)來管理套件,會在下載套件之後放在 vendor 資料夾,並用 composer.lock 檔案來記錄這些套件的相關資訊。

最新的使用方式,請參考 composer 官方文件

以下整理 composer 的基本指令:

  • composer init:建立 composer.json 檔案
  • composer install:安裝 composer.json 中定義的依賴
  • composer update:更新依賴到最新版本
  • composer require package-name:新增依賴套件到專案
  • composer remove package-name:從專案移除依賴套件
  • composer clear-cache:清除 composer 快取
  • composer self-update:更新 composer 本身

Python 版本套件管理實作

Python 早期是以全域下載為主,使用 pip 作為預設套件管理工具。後來發展出 pyenv 版本管理、venv 虛擬環境隔離,後續還有 pipx 與 poetry 等一堆工具。

目前我是用 uv 來統一處理版本管理、套件管理與環境隔離等任務,因此請不要直接下載 Python,而是先從 uv 開始。

版本、套件管理工具:uv

最新的安裝與使用方式,請參考 uv 官方 GitHub

在建立新專案時,uv 會自動建立一系列的檔案,.python-version 記錄 Python 版本,.venv 對應虛擬環境,pyproject.toml 則是套件清單,在下載套件後會安裝至快取與 .venv/lib,並建立 uv.lock 記錄這些套件的資訊。

以下整理 uv「管理 Python 版本」的相關指令:

  • uv python install:安裝最新的 Python 版本
  • uv python install python-version:安裝指定的 Python 版本(如 3.12,不需要加 python@ 前綴)
  • uv python list:查看可用和已安裝的 Python 版本

以及 uv「專案套件管理」的相關指令:

  • uv init:在當前目錄初始化新專案
  • uv add <package-name>:新增套件依賴並更新 lockfile 和環境
  • uv add 'requests==2.31.0':新增指定版本的套件
  • uv add -r requirements.txt:從 requirements.txt 檔案批次新增套件
  • uv remove package-name:移除套件
  • uv sync:手動同步環境與 lockfile,使環境保持最新狀態
  • uv lock:更新 lockfile 但不安裝

最後在執行的時候,只要以下這行指令,就可以取代以前啟用虛擬環境、安裝套件、執行程式、關閉虛擬環境的整個過程:

  • uv run python script-name:在專案環境中執行 Python script

由於之前我裝過四個不同版本的 Python,分別源自系統預設、Homebrew 與 Framework,也沒有使用虛擬環境的經驗,所有套件都是全域安裝,導入過程自然十分痛苦。不過在完成之後,整體環境乾淨非常多,打下很好的開發基礎,避免未來的全域污染問題。

最後整理的架構如下:

系統層:macOS Python 3.9.6(系統預設版本,作為 uv 失效時的備案)
├── uv 管理層
│   ├── Python 3.12.11(主要開發版本)
│   └── Python 3.13.4(新功能測試)
└── 專案層:獨立虛擬環境
    ├── uv 專案
    └── 傳統專案(向後兼容)