Mise Version Manager

There is no AI here. All content is human-authored and tested for accuracy.

Mise vs asdf

Mise and asdf both make it easy to install and switch among programming language versions, but they differ significantly in speed, architecture, and features. Here's a comparison of mise and asdf. Also find out how to migrate from asdf to mise on your Mac.

Mise and asdf are both multi-language version managers, but they differ significantly in speed, architecture, and features. If you're starting a new project, it's recommended to use mise. But if you already use asdf, learn about the differences and the migration path and decide if it is worth migrating.

Choosing a language version manager is one step in setting up your Mac for development. See the Mac development setup guide.

Before you get started

You'll need a terminal application to install and use mise or asdf. Apple includes the Mac terminal but I prefer Warp Terminal. Warp is an easy-to-use terminal application, with AI assistance to help you learn and remember terminal commands. Download Warp Terminal now; it's FREE and worth a try.

Compare performance benchmarks

The most concrete difference between mise and asdf is speed. Mise is demonstrably faster in multiple scenarios.

Measure shell operation overhead

Each time asdf runs a tool, bash shims execute and incur startup overhead. Measuring the cost, asdf adds approximately 120 milliseconds per command execution. For a developer running commands frequently throughout the day, "you get used to it," but it's really not necessary.

Mise is a Rust-compiled binary program with PATH modification instead of per-command shims. This reduces overhead to approximately 5-10 milliseconds when switching directories or displaying the shell prompt. The difference is roughly 10 times faster than asdf.

In interactive terminal work, this feels dramatic. Directory changes that pause for seconds with asdf in large monorepos execute instantly with mise. This translates to a noticeable improvement in developer experience.

Compare installation speed

Tool installations show even starker performance differences. Mise installs packages up to 7 times faster than asdf in real-world scenarios. Installing Elixir or Node.js that might take 5-10 minutes with asdf completes in under 1 minute with mise.

This advantage stems from mise's Rust implementation. Bash scripts have inherent startup and parsing overhead. Compiled Rust code eliminates this penalty. Every time you install new tools or rebuild environments, you'll appreciate the installation speed. In fact, the ease of installation means you're more likely to install a tool to just try it.

Understand the architecture difference

Asdf's design predates concerns about performance overhead. It uses bash shell scripts for everything: shims, plugin system, and tool management. This made asdf portable (bash runs everywhere) and easy for anyone to contribute plugins.

Mise's Rust-based architecture trades shell simplicity for performance and features. A compiled binary program is faster but doesn't run everywhere, so there's more effort involved in distributing it. For users, it doesn't matter and the performance gains are substantial.

Compare architecture and design

Understanding how these tools work internally explains their performance and capability differences.

Understand asdf's Bash foundation

Asdf is written entirely in bash shell scripting. When you use asdf, you're running bash functions and scripts that execute each time you call a tool. Every tool call incurs bash startup and script parsing overhead.

Asdf plugins are bash scripts that know how to install, list versions, and locate binary programs for specific tools. This makes plugins simple to write, which is why asdf has hundreds of community plugins. However, bash's limitations constrain what plugins can do and how efficiently they can work.

The shim approach works by creating tiny bash scripts ("shims") in ~/.asdf/shims that intercept tool calls. For example, when you run node, you're actually running an asdf bash shim that determines which Node.js version is active, then launches the real binary program. This process happens on every invocation. You'll notice when you run a which command, showing the asdf shim path rather than the actual location of the executable for a program.

Understand Mise's Rust implementation

Mise is written in Rust, a compiled systems programming language. The entire tool is a single compiled binary program distributed for different operating systems and architectures. When you install mise, you get a pre-compiled executable, not scripts that need to be interpreted.

Mise's PATH modification approach is more efficient. Instead of shims intercepting each call, mise modifies your PATH before showing your shell prompt. This means tools are found directly without wrapper scripts. The compiled binary program handles version resolution quickly, then your shell runs the actual tool. Note: the PATH approach is used on local systems; CI on remote servers (and some IDEs) use the mise shim approac that's similar to asdf.

Plugins in mise can be built on a more flexible system. While mise supports asdf plugins for compatibility, native mise plugins use a modern plugin infrastructure with better performance characteristics.

Consider implications for maintenance and features

The architectural difference affects what features each tool can realistically support. Bash has limitations on what it can easily express. Adding complex features or improving performance requires working within bash's constraints.

Rust, as a modern language, supports more sophisticated features naturally. Mise can include built-in environment variable management, a task runner system, and advanced configuration options without the awkward workarounds bash would require.

For long-term maintenance, a compiled binary program with a focused codebase is easier to maintain and improve than distributed bash scripts. This is why mise development velocity is higher than asdf's.

Compare configuration with .mise.toml or .tool-versions

The configuration format difference reflects each tool's philosophy.

The asdf configuration file

Asdf uses .tool-versions, a minimal text format with one tool per line.

node 24.0.0
python 3.11.0
go 1.20

This format is extremely simple to read and write. It's also easy to parse and has minimal syntax to learn. For teams that only need version specification, .tool-versions is sufficient.

The limitation is that .tool-versions does only one thing: specify versions. It doesn't support environment variables, settings, or structured configuration. If your project needs to set NODE_ENV=development or customize tool behavior, you need separate configuration files outside version management.

The Mise configuration file

Mise uses .mise.toml, a TOML-formatted configuration file with distinct sections for different purposes. See Mise Configuration.

[tools]
node = "24"
python = "3.11"

[env]
NODE_ENV = "development"
DATABASE_URL = "postgres://localhost/db"

[settings]
log_level = "info"

This format handles versions, environment variables, and settings in one place. TOML provides clear structure with sections and readable syntax. Comments are supported, making configuration self-documenting.

The trade-off is that .mise.toml is more complex than .tool-versions. Projects that only need version specification might find .mise.toml unnecessary. However, you can edit the file with a text editor or the built-in mise edit command which simplifies configuration.

Maintain backward compatibility

Mise reads asdf .tool-versions files without any conversion. If you have an existing asdf setup, mise works immediately with your .tool-versions files . Asdf cannot read .mise.toml files. The conversion is one-way: you can migrate from asdf to mise, but not vice versa.

If a project uses .tool-versions and you run mise use node@24 to update it, mise might write a fuzzy version (like 24) while asdf expects exact versions. The --pin flag preserves exact version format for asdf compatibility.

Plugins

The plugin situation is asdf's primary advantage.

Asdf's plugin ecosystem

Asdf has hundreds of community-maintained plugins covering every programming language and tool imaginable. If you use an obscure language or tool, asdf likely has a plugin for it.

These plugins vary in quality and maintenance. Some are excellent and actively maintained. Others are outdated or barely functional. Plugin quality can be a concern for asdf's users.

Mise's native plugins

Mise provides hundreds of official, optimized plugins, including all common languages: Node.js, Python, Ruby, Go, Rust, Java, Erlang, and many others. These native plugins are faster and more reliable than asdf equivalents because they're maintained alongside mise and can leverage its architecture.

Mise has fewer plugins overall, but coverage of common tools is excellent. For niche languages, missing plugins are the main limitation.

Use asdf plugins in Mise

Mise can use asdf plugins as a fallback. When you request a tool without a native mise plugin, mise attempts to load the asdf plugin for that tool. Compatibility is best-effort, not guaranteed. Some plugins work fine. Others have issues or behave unexpectedly.

The compatibility mode is useful for team transitions. You can migrate to mise before all asdf plugins are available as native plugins. Teams gradually move to native plugins for better performance.

Compare Mise to language-specific managers

Often developers install a dedicated version manager for each language (for example, nvm for Node.js, pyenv for Python, rbenv for Ruby) when they first encounter the need to support multiple versions of a language. Understanding how Mise and asdf compare to dedicated managers clarifies their value.

nvm vs Mise for Node.js

See details in my article Node.js with Mise.

NVM is Node.js's dedicated version manager. It is well-documented and widely used by Node.js developers. NVM adds approximately 50 milliseconds to shell startup time. Mise manages Node.js plus all other languages. Mise's performance advantage becomes clear when combining nvm with pyenv and rbenv. Using all three adds ~110 milliseconds to shell startup. Mise achieves this with ~10 milliseconds overhead.

With NVM, version info lives in .nvmrc while environment variables and tool‑level settings usually live in .env files or shell dotfiles. Mise lets you keep versions, environment variables, and project settings together in a single .mise.toml file.

NVM typically relies on nvm use (or plugins like avn/shell hooks) to switch Node versions when you cd into a project, which adds more shell scripting and latency. Mise bakes directory‑aware activation into mise activate, so it automatically switches versions on directory change with a single, optimized hook.

For Node.js-only projects, nvm might be sufficient. For projects mixing Node.js with Python, Ruby, or other languages, mise eliminates the need for multiple tools.

uv or pyenv vs Mise for Python

See details in my article Python with Mise.

You can Install Python with Pyenv, Python's dedicated version manager, using .python-version files to control which version applies globally, per user, or per project. More recently, developers install Python with uv which is a fast Rust-based version manager that handles Python versioning and virtual environments.

You must use Python virtual environments with Pyenv to isolate packages rather than language versions, using tools like venv, virtualenv, or  yenv-virtualenv. Mise doesn’t remove the need for virtual environments, but it can automatically create and activate a project’s .venv or uv‑based environment based on configuration, which reduces manual python -m venv and source .venv/bin/activate steps and makes environment handling feel integrated instead of bolted on.

rbenv vs Mise for Ruby

See details in my article Ruby with Mise.

Rbenv is a Ruby‑only version manager that uses shims and .ruby-version files to switch interpreters per project, keeping its scope narrow and its behavior familiar to long‑time Ruby developers. Mise provides the same Ruby version switching while reading existing .ruby-version files, but it adds a broader, multi‑language workflow driven by .mise.toml so you can manage other languages the same way.

Gem isolation works similarly in both tools, with each Ruby version keeping its own gem directory, but Mise adds built‑in environment scoping and a task runner so you can automate bundle exec commands and env vars with a selected Ruby version when switching projects.

The rv tool is a newer, high‑performance Ruby version manager that aims to do for Ruby what tools like uv do for Python, focusing on speed and integrated dependency handling. Mise can use the precompiled Ruby binaries from rv to speed up installation and switching and, unlike rv, can manage multiple languages.

Older Ruby on Rails projects typically need rbenv for Ruby plus nvm for Node.js for asset compilation and JavaScript. For Rails developers, it makes sense to use Mise as a single tool for both languages.

SDKMAN vs Mise for Java

See details in my article Java with Mise.

SDKMAN is a version manager for managing Java versions and distributions, switching between JDKs from many vendors. It also manages Java tools such as Maven, Gradle, Kotlin, and Groovy with a sdk install and sdk use workflow.

SDKMAN’s strengths are its deep integration with Java tooling and its popularity among Java‑only teams, but it is a shell‑based script that adds noticeable overhead to shell startup and requires you to remember a separate command set. Mise is a single tool that can switch Java versions and reset the JAVA_HOME environment variable when you switch projects.

If you're only working with Java, you may not need to switch language versions, or you can switch manually by changing the JAVA_HOME environment variable, or you can use SDKMAN, or switch to Mise for a single tool that has good developer ergonomics.

Benefits of a single version manager for multiple languages

The real value of mise (and asdf) is consolidation. Many workplace projects use multiple languages. A team using Ruby, Python, and Node.js would traditionally need rbenv, pyenv, and nvm installed and configured separately.

Consolidating into mise means one tool, one activation method, one command syntax, and one configuration file. This simplifies onboarding, reduces shell startup time, and makes version management intuitive.

Migrate from asdf to Mise

Moving from asdf to mise is straightforward because mise reads .tool-versions files and can use asdf plugins. However, planning ensures a smooth transition.

Prepare for migration

Before migrating, back up your asdf configuration and document which asdf plugins you actively use.

$ asdf plugin list

This shows your current plugins. If you have custom plugins, note them. Most teams use only common language plugins that have mise equivalents.

Run the step-by-step migration

Install Mise on your Mac using the Homebrew or curl method. Follow the installation guide to activate mise in your shell.

Test that mise can read your existing .tool-versions files.

$ mise ls

This command lists all languages or tools in your .tool-versions file. If you see your languages and tools, mise is reading your configuration correctly.

It's easiest to run mise edit to read the .tool-versions file and create a .mise.toml file that contains everything from .tool-versions. This way you don't need to manually convert your configuration. See Mise Configuration. You can import an existing asdf .tool-versions file:

$ mise edit -t .tool-versions

Alternatively, install the specified versions with mise.

$ mise install

This command installs all tools listed in .tool-versions to mise's directory (~/.local/share/mise/installs/). Note that tools are installed separately by mise, not migrated from asdf's directory. You'll have duplicate tool installations during the transition, which requires extra disk space temporarily.

Verify that tools work correctly.

$ node --version
$ python --version
$ ruby --version

All tools should respond with version numbers. If something is wrong, mise doctor provides diagnostics.

For new projects or when ready, create .mise.toml files.

$ mise use node@24
$ mise use [email protected]

These commands create .mise.toml in your project with the specified versions. Gradually convert existing projects to .mise.toml format.

When your entire team uses mise, remove asdf.

$ brew uninstall asdf --force
$ rm -rf ~/.asdf

Remove asdf activation from your .zshrc shell configuration file.

Keep both tools temporarily

During team transitions, run mise and asdf in parallel. Both tools can coexist without conflict if you manage PATH carefully.

Set up separate shell activation for each tool.

# In ~/.zshrc, first activate asdf, then mise
eval "$(asdf env)"
eval "$(mise activate zsh)"

The key is having mise activation occur after asdf. This way, mise's PATH modifications take precedence. Projects with .mise.toml use mise versions, while projects with only .tool-versions fall back to asdf.

This approach allows team members to migrate individually. Some developers use mise on new projects while others stay with asdf on legacy projects. Eventually, everyone transitions, and asdf can be removed.

Clean up files after migration

After migration, you can delete asdf-related files. The ~/.asdf directory contains asdf configuration and tool installations. This directory can be deleted once you've verified all tools are installed via mise.

Note that tool installations in ~/.asdf/installs/ are not automatically migrated. Mise installs tools to a different location (~/.local/share/mise/installs/). If you have custom tool builds in asdf, you might need to rebuild them with mise.

Why to keep asdf

Despite mise's advantages, asdf remains suitable for some scenarios.

Mature team infrastructure

Large teams with established asdf setups have built configurations, documentation, and processes around asdf. Migrating might disrupt these established practices. The migration effort might not be worth the performance gains for teams that rarely encounter asdf's performance problems.

Custom asdf plugins

Teams that developed custom asdf plugins specific to internal tools face a decision. Porting plugins to mise format requires effort. Using asdf plugins in mise compatibility mode works but sacrifices performance gains.

Edge case compatibility

Some unusual asdf plugin syntax or configuration might not work in mise compatibility mode. If your setup relies on advanced asdf features not supported in mise, staying with asdf is necessary. These cases are uncommon but be sure to test compatibility before migrating.

Mise advantages

Assuming you're not locked into asdf by established infrastructure, mise offers substantial benefits.

Performance

Mise's ~10ms overhead versus asdf's ~120ms is a tenfold improvement. Directory switching in monorepos becomes instant instead of slow. Installation speed of 7x faster reduces setup and rebuild times dramatically.

Modern architecture

Rust's compiled architecture is more maintainable than bash scripting. The mise codebase is cleaner and easier to extend than asdf's distributed scripts.

Features

Environment variables, task running, and structured configuration don't require workarounds in mise. Asdf users patch missing features with separate tools.

Development velocity

Mise development is more active than asdf's. New features land faster, and issues receive quicker attention.

Choosing between Mise and asdf

If you're starting a new project, choose mise. Unless you have specific reasons to choose asdf, mise is the better modern choice.

If you have an established asdf setup and are happy with it, migration isn't urgent. However, when you're refreshing infrastructure or onboarding new team members, consider migration to mise.

Continue setting up your Mac

Don't miss the full visual roadmap and checklist that shows how to set up a Mac for software development, with all the essential tools and settings you might not yet know about.