開發工具 Rust Mise asdf 版本管理

asdf 0.16 改寫成 Go 才追上速度:Mise 用 Rust 把版本、env、task 一次收進同一支 binary

asdf 從 bash 改寫成 Go 之後 shim 開銷終於壓下來,但 Mise 早就用 Rust 把 PATH 切換、環境變數載入、task runner 三件事收進一個 binary。本文解析 Mise 的架構選擇、跟 asdf 0.16 的實質差異,以及在多語言專案裡實際的設定方式。

每個有點規模的後端團隊,桌面上的專案多半都是混雜的。一個 Node 18 的 API、一個 Python 3.12 的資料 ETL、一個 Go 1.22 寫的 CLI、再加上某個老舊 Rails 5 的內部報表系統。每次切到不同 repo,shell 裡的 node --versionpython --version 都得跟著換,這就是版本管理工具存在的理由。

過去十年這塊地盤幾乎是 asdf 在守。它用 bash 寫的 plugin 架構把 nvm、pyenv、rbenv、gvm 等各語言專屬工具收成一個介面。問題是 bash 跟 shim 這兩個基底實作起來,現代開發者越來越受不了——每執行一條指令多 120ms 的延遲、每次 cd 進 monorepo 卡個幾百毫秒,累積一整天就是真實的工時損失。asdf 2025 年底發布 0.16 版本,整個核心從 bash 改寫成 Go,效能終於追了上來。但這個時間點,Mise 已經把版本管理、環境變數、task runner 三件事都收進同一支 Rust binary,而且累積到接近三萬顆 GitHub 星。

shim 這個架構從根上就慢

asdf 之所以能支援所有語言,靠的是 shim 機制:當執行 node 時,實際呼叫到的是 ~/.asdf/shims/node 這個 bash 腳本,腳本去讀當前目錄的 .tool-versions、解析該用哪個版本、再轉發到真正的執行檔。這個設計很優雅,但每次都要付出代價——bash 解釋器啟動、檔案 I/O、字串解析,全部加起來大約 120 毫秒。

對人工敲指令的場景,120ms 還在容忍範圍內。但編輯器、Language Server、Makefile、CI script 都會大量呼叫 nodenpmpython,這個延遲會被乘上幾十倍。LSP 啟動一次跑 30 個 sub-process,光是 shim 開銷就占掉 3.6 秒。寫 React 專案的人應該都有過「按下儲存後游標卡住一拍」的經驗,那個拍子的相當部分就是 shim 解析吃掉的。

Mise 的解法是徹底捨棄 shim。它直接修改 shell 的 PATH 環境變數,把當前專案該用的版本路徑塞進去,呼叫 node 就是直接打到對應版本的二進位檔,中間沒有任何代理層。cd 進新目錄時觸發一個用 Rust 寫的偵測函式,沒變動就 4ms 結束、需要重載也只要 14ms。對比之下 asdf 0.16 改成 Go 之後,directory hook 也壓到了個位數毫秒,這部分差距確實縮小了。

但效能不是 Mise 真正的賣點

asdf 在 0.16 用 Go 重寫之後,純版本管理場景的效能差距已經沒那麼明顯。Mise 仍然快,但快得不夠多到值得整組團隊換工具。真正讓 Mise 成為 2026 年新專案預設選擇的,是它把另外兩件事一起做了。

第一件是環境變數管理。一個典型的 Node 專案會有 .env 載入 DATABASE_URL、Python 專案會有 .venv/bin/activate 設定路徑、Go 專案會有 GOFLAGS。傳統做法是搭配 direnv 之類的工具,但 direnv 又是另一個要裝、要設定、要除錯的元件。Mise 直接把這層收進 mise.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
[tools]
node = "20.11.0"
python = "3.12"
go = "1.22"

[env]
DATABASE_URL = "postgresql://localhost/myapp_dev"
NODE_ENV = "development"
_.file = ".env"
_.source = "scripts/setup.sh"

[env.PATH]
prepend = ["./node_modules/.bin"]

_.file 自動載入 .env 檔,_.source 執行 shell 腳本並擷取其匯出的變數,PATH 操作則用結構化語法處理,不必再寫一堆 bash 拼接。對於把密鑰存在 1Password CLI 或 AWS Secrets Manager 的團隊,Mise 也有原生整合可以動態抓進來,不會落地到磁碟。

第二件是 task runner。寫 Makefile、寫 npm script、寫 justfile,每個專案的慣例都不同。Mise 提供統一的 task 語法,定義在同一個 mise.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[tasks.build]
description = "Build the production bundle"
run = "npm run build"

[tasks.test]
description = "Run tests with coverage"
depends = ["build"]
run = """
npm test -- --coverage
python -m pytest tests/integration
"""

[tasks.deploy]
description = "Deploy to staging"
depends = ["test"]
env = { TARGET_ENV = "staging" }
run = "./scripts/deploy.sh"

mise run test 就能跑,mise tasks 列出所有可用任務。對於剛接手陌生 repo 的新成員,這個 self-documenting 的特性比翻 README 找指令快得多。task 之間還能用 depends 串依賴關係,等於把 Makefile 的功能用更現代的語法重做了一次。

跟既有工具的相容性處理得相當細

工具切換最怕的是搬家成本。Mise 在這方面做得意外乾淨——直接讀 .tool-versions、直接吃 asdf plugin、.nvmrc.python-version.ruby-version 全部支援。從 asdf 切過去通常只需要兩步:裝 Mise、註冊 shell hook,原有的 .tool-versions 不用改。

對需要保留多種工具同時運作的環境,Mise 也不會搶 PATH。它只在偵測到當前目錄屬於某個 mise 管理的專案時才注入路徑,離開該目錄就還原。這代表 nvm 或 pyenv 沒有完全移除也不會打架,可以漸進式遷移。

Plugin 安全性是另一個值得提的差異。asdf plugin 本質上是 bash 腳本,裝一個 plugin 等於授權對方在你 shell 裡執行任意指令,過去也確實出過 plugin repo 被棄置後遭收購、塞惡意程式碼的事件。Mise 預設使用內建的 backend,950 個常用工具直接從官方來源驗證 GPG、Cosign、SLSA 簽章下載,不必經過第三方腳本。需要冷門工具時還是可以裝 asdf plugin,但這個選擇是顯式的,不是預設值。

什麼時候該繼續用 nvm 或 pyenv

不是所有人都該換 Mise。三種場景反而適合留在單一語言的版本管理工具:純前端團隊只開 Node 專案、原本就習慣 nvm 的 shell 整合,沒理由把學習成本花在 Mise;資料科學環境深度綁定 conda 或 uv,這兩個的虛擬環境機制比 Mise 的 venv 管理更完整;CI runner 容器化以後本來就只裝一個語言版本,多包一層 Mise 反而增加 image 大小。

但只要專案開始混雜兩種以上語言、或團隊有多個專案需要不同版本,Mise 的整合價值就會浮出來。mise.toml 進 git 之後,新人 clone 完 repo 跑 mise install 就拿到一致的工具版本、環境變數、task 列表,不必再寫一份「開發環境設定」的 README 還跟維護現實脫節。

在伺服器上怎麼用

Mise 不只是本機開發工具,伺服器端的部署流程也用得到。常見的兩種場景:CI runner 跟自架 build server。

CI 場景下,把 mise.toml 進 repo 後,CI 腳本第一行 mise install 就能裝齊本次 build 需要的所有工具版本。比 GitHub Actions 那種 actions/setup-node 系列要快——後者每個語言要一個 action、每個都跑一次網路請求;Mise 一次解析完整個專案的需求一起裝,並把結果快取在 ~/.local/share/mise/,下次 build 直接用。

自架 build server 上更明顯。一臺機器上同時跑多個 repo 的 CI job,每個 repo 用不同語言版本,Mise 用 PATH 切換就能做到隔離,不必為每個版本組合開不同的 container。對自己維運 CI 機房的團隊,這是省下不少 image 維護成本的設計。

結論

asdf 用 0.16 補上了 bash 改 Go 這個遲到的修補,但 Mise 已經把問題的定義拉大——版本管理只是入口,環境變數跟 task 都是配套的同一件事。對新專案來說,預設選 Mise 已經沒有什麼好猶豫的;既有 asdf 環境的團隊則可以利用兩者的相容性,挑一個低風險時機切過去。

執行 CI runner、build server、跑開發環境,這些都是吃 CPU 跟 I/O 的工作負載。NCSE Network 在臺灣是方電訊機房提供 Intel Gold CPU 加 NVMe SSD 的 VPS 主機,本地網路延遲低,跑自架 CI、共享 build server 或多開發者協作環境的反應速度都相當穩定,是把 Mise 工作流搬上雲端的合理基礎。

需要技術開發支援?

NCSE Network 提供 Discord Bot、LINE Bot、AI Agent、爬蟲、監控系統等客製化開發服務,從規劃到上線一站式完成。

洽談專案 →