Mise vs asdf: Which Version Manager Should You Choose?
Managing different language versions across projects gets messy fast. One project needs Node 18, another requires Node 16, and that legacy app still runs on Node 14. Version managers promise to solve this chaos, but mise and asdf take completely different approaches to the same problem.
asdf pioneered the unified version manager concept. It handles multiple languages through a plugin system, giving you one tool for Node, Ruby, Python, and dozens of other runtimes. Each plugin follows asdf's conventions, creating a consistent experience across all your tools.
mise started as asdf's spiritual successor but diverged into something different. It matches asdf's core functionality while adding environment variable management, task running, and built-in support for popular languages. The tool aims to replace not just asdf, but also direnv and make.
Choosing between them means deciding what you value more: asdf's focused, stable approach to version management, or mise's expanding feature set that handles more of your development workflow. Let's look at how they actually work in practice.
What is asdf?
asdf is a version manager that handles multiple programming languages through a plugin architecture. Created in 2014 to replace separate version managers like rbenv, nvm, and pyenv, asdf gives you one tool that works the same way for every language.
The plugin system forms the core of asdf. When you need a new language, you install its plugin, then use the same commands you already know. Each plugin maintainer handles language-specific installation details while asdf provides the consistent interface.
asdf reads .tool-versions files in your project directories. When you enter a folder, asdf automatically switches to the versions specified in that file. This approach keeps different projects isolated without manual intervention.
What is mise?
mise is a version manager that started as a Rust rewrite of asdf but evolved into a broader development tool. Created by Jeff Dickey in 2022, mise handles version management while also managing environment variables and running project tasks.
The tool maintains compatibility with asdf's plugin ecosystem. You can use most asdf plugins with mise, getting access to hundreds of languages and tools without starting from scratch. mise also includes built-in support for major languages, eliminating plugins entirely for common cases.
mise goes beyond version switching. It replaces direnv for environment variable management and provides a task runner for common project commands. This consolidation means fewer tools to install and configure in your development environment.
mise vs asdf: quick comparison
| Feature | mise | asdf |
|---|---|---|
| Installation method | Single binary, package managers | Git clone, shell sourcing |
| Plugin compatibility | Uses asdf plugins | Native plugin system |
| Built-in languages | Node, Python, Ruby, others | Requires plugins for everything |
| Environment variables | Native support (replaces direnv) | Requires separate tools |
| Task runner | Built-in task system | None, use make or similar |
| Configuration files | Multiple formats (.mise.toml, .tool-versions) | .tool-versions only |
| Shell integration | Automatic activation | Manual shell configuration |
| Legacy project support | Reads .tool-versions files | Native format |
| Community size | Growing, newer project | Large, established community |
| Breaking changes | More frequent updates | Stable, careful changes |
| Implementation | Rust | Go (as of v0.16.0) |
Installation and initial setup
I tried both tools on a fresh development machine to see how quickly I could get started. The installation experience revealed a lot about each tool's design priorities.
asdf requires shell integration during setup:
You need to add asdf to your shell's configuration file, then restart your terminal. This integration lets asdf intercept commands and switch versions automatically. The manual setup feels a bit dated, but it works reliably once configured.
mise installs as a standalone binary:
mise's activation step happens with a single command that you add to your shell config. The single binary approach means faster startup times and simpler updates through your package manager.
Managing your first language
After getting both tools installed, I wanted to see how the actual version management worked. I needed to set up Node for a new project.
asdf requires installing a plugin first:
Every language starts with a plugin installation. This two-step process (plugin, then language) feels repetitive when you're setting up multiple languages, but it keeps asdf's core minimal. The list all command can be slow because it queries the upstream source each time.
mise streamlines the common cases:
mise includes popular languages like Node, Python, and Ruby directly. You skip the plugin step entirely for these common tools. The use command installs and activates versions in one go, making the initial setup faster.
Configuration file formats
That simplified mise use command compared to asdf's multi-step plugin installation got me wondering about how each tool stored these settings. I opened up the project files to see what was actually happening behind the scenes.
asdf uses a simple, universal format:
The .tool-versions file lists each tool with its version on a single line. This format works everywhere asdf works and never changes. Other tools can easily parse it, and you can edit it by hand without consulting documentation.
mise supports multiple configuration formats:
mise's TOML format includes environment variables and task definitions alongside version specifications. This consolidation keeps related configuration together, but it means learning a new format. mise also reads .tool-versions files for compatibility with asdf and projects that use that format.
Switching between versions
Those configuration files showed me that mise wanted to do more than just version management, but I still needed to see how the core functionality compared. I set up a few projects with different Node versions to test the switching behavior.
asdf switches versions automatically when you change directories:
asdf reads the .tool-versions file in each directory and activates those versions. The current command shows which versions are active and where they came from. This works reliably across different shell sessions.
mise provides the same automatic switching:
mise's version switching works identically to asdf since both tools use the same directory-scanning approach. The ls command provides more detailed output about where each version came from and what was originally requested versus what's installed.
Environment variable management
The automatic directory switching worked well with both tools, but I kept thinking about that .mise.toml file with environment variables mixed in. I decided to test whether mise's expanded scope actually made development easier or just added complexity.
asdf doesn't handle environment variables directly:
You install direnv separately and create .envrc files for environment variables. This separation means maintaining two tools and two configuration files, but it keeps concerns separated cleanly.
mise manages environment variables natively:
mise loads environment variables automatically when you enter the directory. You can define them inline, load them from files, or modify PATH. This consolidation means one less tool to install, though it couples version management with environment configuration.
Running project tasks
Watching mise automatically load environment variables made me curious about its task runner. I had been using npm scripts and make files to run common commands, and I wanted to see if mise's task system could replace them.
asdf has no task running features:
You bring your own task runner: make, npm scripts, just, or whatever fits your project. asdf stays focused on version management and doesn't try to replace these tools.
mise includes a built-in task runner:
mise executes tasks with access to the project's environment variables and tool versions. You define tasks in the same file as your tool versions, keeping everything together. The task system supports dependencies, custom environments, and multi-line commands.
Plugin ecosystem and compatibility
That task runner felt convenient, but I still needed languages beyond Node and Python. I wanted to see how well mise's asdf plugin compatibility actually worked with the hundreds of plugins the asdf community had created.
asdf has an extensive plugin ecosystem:
The asdf community maintains over 500 plugins for languages, tools, and utilities. Each plugin gets reviewed before joining the official list. However, plugin quality varies, and some require system dependencies that aren't always documented clearly.
mise uses asdf plugins with some caveats:
mise includes native support for Node, Python, Ruby, Java, Go, and others. You don't install plugins for these languages—they work immediately. For other tools, mise can use asdf plugins directly. Most plugins work fine, though some might have issues because mise isn't a perfect clone of asdf.
Handling version installation
I wondered if mise and asdf differed in how they installed language versions. I decided to install the same versions of several languages with both tools to see if the process varied.
asdf downloads and compiles from source for many languages:
Some asdf plugins compile languages from source, taking minutes to install. Others download pre-built binaries. The experience varies depending on which plugin you're using and whether the maintainer provides binaries for your platform.
mise takes a similar approach:
Since mise uses asdf plugins for many languages, installation times match closely. The built-in support for major languages might eventually include better caching or faster installation methods, but right now it's comparable to asdf.
Update and maintenance patterns
Those long Ruby installation times reminded me that keeping tools updated matters too. I wanted to see how each tool handled updates—both to the version manager itself and to the language versions it managed.
asdf updates through git:
You update asdf by pulling the latest git changes. Plugin updates happen separately, and you manually check if newer language versions are available. The separation between tool updates and version updates gives you control but requires active maintenance.
mise updates like any other binary:
mise updates through your package manager or its self-update command. The outdated command shows newer versions at a glance, and upgrade updates everything automatically. This convenience comes with a tradeoff—automatic updates might install versions that break your projects.
Working with multiple projects
After seeing how both tools handled updates, I wanted to test something closer to real work: managing several projects that all needed different versions of the same languages. This is where version managers either shine or fall apart.
asdf handles this through directory-specific configuration:
Each project directory gets its own .tool-versions file. When you move between projects, asdf switches versions automatically. The global setting provides a default for directories without their own configuration.
mise works the same way with additional options:
mise reads .tool-versions files just like asdf, but it also supports its own .mise.toml format. The --path option lets you set defaults for entire directory trees, which helps when multiple projects should use the same versions.
Shell integration differences
Managing multiple projects showed me that version switching worked smoothly with both tools, but I started wondering about the underlying shell integration. This integration determines how reliably version switching works across different shells and terminal sessions.
asdf requires explicit shell configuration:
You add asdf to your shell's startup file manually. This explicit configuration means you know exactly what's happening, but it requires setup for each shell you use. The hooks asdf installs intercept your commands and modify PATH to point at the right versions.
mise provides automatic shell activation:
mise's activation sets up shell hooks automatically. The Rust implementation makes these hooks efficient. You still modify your shell configuration, but mise handles the details of hooking into your shell's command execution.
Final thoughts
This article compared mise and asdf. asdf is great at simple version management, but mise goes further. It brings versions, environment variables, and tasks into one tool, which makes your workflow more cohesive. The trade-off is a little more complexity, but the flexibility and convenience usually outweigh that. If you want more than just version switching, mise is the better choice.