blog.src.rip

mise and chezmoi will change your life

Featuring: why mise is so much better than asdf for Elixir engineers.

10/7/2026

This is a simple public service announcement. I’ve used asdf for quite a long time now. But no longer. Relationship over. My friendship with asdf is severed. mise is my friend now. mise and chezmoi. In fact, let’s start with chezmoi:

chezmoi

chezmoi means “my house” in French. So you can now let everyone know that when the topic comes up. By the way, chez moi means “my house” in French.

Anyways, there are about a billion different dotfile manager programs out there. It’s so bad, the one I used to use was literally called yet-another-dot-manager, yadm. It was ok. It got the job done.

The basic model behind chezmoi is this—there are three states:

  1. Source State: these are files that live in the “source directory”, by default this is ~/.local/share/chezmoi, but you can configure this, and I personally configure mine to be ~/dev/dotfiles. This is where you would initialize your git repository that you push to GitHub, and this stores the “canonical” version of your dotfiles.
  2. Destination State: This is the files you are looking to “replace” with the version in your dotfiles, such as your Neovim config at ~/.config/nvim/init.lua. This file would also be called the “target” file in chezmoi parlance.
  3. Target State: This is the final file put in the target space at the destination, after chezmoi compares the target file to what’s at the source location.

This is pretty different than how most dotfile managers work. Most dotfile manager programs do one of two things:

  1. Create an actual git repository in your home directory. This is by far the simplest approach. You barely need a special program here at all aside from git, although there are some that extend this approach a bit. Adding files is a simple git add ~/.config/nvim/init.lua. Unfortunately, having your home directory be one giant git directory comes with some downsides though. Git doesn’t handle it the best, and the blast radius of a bad git reset or something is pretty big.
  2. Create a git repository somewhere else, and symlink the important files into that repository. I would say this is the most common approach, and there are many dotfile manager programs that basically work this way. It solves the biggest problems with the “just make your home directory a git repository” method. Its biggest downsides are just fiddliness with symlinks themselves—some programs don’t always handle them the best. And there is of course the added step of putting the links in place. But this method works pretty well, and it’s what a lot of people do.

chezmoi behaves like option 2, but with copying files into place instead of symlinking them. This sidesteps the issues inherent to symlinks, which is nice, and when you have a program performing an actual copy step for you, you can add a couple things which are pretty damn nice to have in a dotfile manager:

In chezmoi, since there is this copy step, there is actually quite a lot of different workflows you can adopt. There’s 5 described methods in the FAQ, which I will quote here:

  1. Use chezmoi edit $FILE. This will open the source file for $FILE in your editor, including opening the template if the file is templated and transparently decrypting and re-encrypting it if it is encrypted. For extra ease, use chezmoi edit --apply $FILE to apply the changes when you quit your editor, and chezmoi edit --watch $FILE to apply the changes whenever you save the file.
  2. Use chezmoi cd and edit the files in the source directory directly. Run chezmoi diff to see what changes would be made, and chezmoi apply to make the changes.
  3. If your editor supports opening directories, run chezmoi edit with no arguments to open the source directory.
  4. Edit the file in your home directory, and then either re-add it by running chezmoi add $FILE or chezmoi re-add.
  5. Edit the file in your home directory, and then merge your changes with source state by running chezmoi merge $FILE.

It is also worth reading the daily operations guide, as it covers a lot of this territory too. chezmoi can have kind of a lot of CLI commands to wrap your head around, but as you can see, it’s because you have a lot of options in whether you want to edit the source directory and then apply your changes (chezmoi apply), or edit the destination and then track the changes back up to the source directory (chezmoi re-add).

💡

It’s worth noting that chezmoi re-add does not work on templated destination files, so if you were to edit one of those and need to get the changes tracked back in your source directory, you would need to use chezmoi merge, which does kind of a 3-way merge thing to reconcile state between the source and destination directories.

It may sound kind of daunting here, but it’s actually pretty easy to work with day-to-day. Kind of like git itself. I don’t know why, but it’s the first dotfile manager that ever really clicked for me in a way that made me go “oh yeah, this is what I want to keep using.”

mise

Alright, onto mise (more French alert). mise is something I saw people talking about for a long time, but for some reason I didn’t try switching to it until incredibly recently. I guess I assumed it was just like asdf, but maybe marginally better, so why switch?

Well before getting into the details, let me just say that if you are an Elixir engineer like me, you absolutely must be using mise. It is so much more convenient than asdf for managing versions of Elixir and Erlang.

For reasons that are not entirely clear to me in Current Year, asdf insists on compiling Erlang from source. As I understand it, the story is basically this:

  • In the long ago times, before asdf, programs like rvm for Ruby or nvm for Node started becoming popular for building versions of those programming language runtimes. Someone built a tool called kerl, which was basically that but for Erlang.
  • When asdf was being built, it basically just papered over you having to manage all these disparate programming-language-managers for you, so of course its Erlang plugin simply wrapped kerl.
  • In the Current Times, it has become much more popular to distribute binaries for every conceivable OS/platform out there. That is exactly what Bob is for the Elixir/Erlang ecosystem. You can look at the Bob dashboard here, and see all of the different versions of Erlang and Elixir it’s built recently.
  • That’s pretty cool! Thankfully, mise decided to use Bob by default, and only fallback to kerl if it can’t find the right binary for you. Thank God. What used to take several minutes (compiling Erlang) now takes a couple seconds on a good internet connection.

I can’t emphasize how much time of my life I’ve wasted sitting there, watching kerl, by way of asdf, building Erlang. I have no idea why asdf hasn’t switched over to using Bob as well. I’m sure there’s some reason, but whatever. This in and of itself is enough reason for every Elixir engineer to dump asdf like a hot potato and switch over to mise.

But that is I guess a pretty unique problem to us Elixir engineers. What else makes mise different?

If you haven’t read it, I direct you to read the website cause it’s very well made. mise does quite a lot. It is a programming-language-runtime-installer-thing, but it is much more too. It describes mise as being able to take over 4 roles for you:

  1. As described, it can replace programming language installers like asdf, nvm, rvm, rbenv, etc.
  2. It can replace direnv, which lets you set environment variables for different project directories.
  3. It can replace make, allowing you to run project build/test/lint/whatever commands.
  4. It can even replace chezmoi itself, and be your new machine bootstrapping command.

mise, as you can see, kind of does a lot. It would take a long time to go over it’s many features. I will just touch on a couple more things I find remarkable outside of the joy of no longer having to compile Erlang.

First off, at its main task of managing programming languages and other programs you might need, it does so with, in my opinion, a much better API than asdf. The API of asdf, I think it is fair to say, is kind of a mess. I think the original version was written completely as bash scripts, and it kind of shows. mise just makes a lot more sense to use day-to-day.

Secondly, mise, as far as I can tell is still based on a plugin-like architecture, but they are either all built-in by default or fetched as soon as they are needed. With asdf, you need to go fetch the plugin you want to use for the first time (for Erlang, NodeJS, Golang, whatever) before you can use it. With mise you can just do mise use erlang@latest on a new setup and it just works.

Finally, I quite enjoy the way that mise can install many, many tools that are not just programming languages. For instance, you probably use fzf in your daily programming life (if you don’t, you should probably start now). Well you can install and manage updates for fzf with mise too. mise has a rather sophisticated system it calls “backends” for installing these tools, which you should read about here. It has a concept called its “registry”, which defines a list of these tools, and a series of backends that get used in decreasing order of preference. It tries to use a thing called Packslips (which I hadn’t heard about til now), which are apparently a signed code release thing first, followed by stuff like looking at GitHub binary releases. The whole thing is really cool, and it’s nice being able to take stuff like fzf out of the hands of something like Homebrew and into something a little bit better like mise.

Two great tastes that taste great together

mise has been getting into the dotfiles game, as I described above, so theoretically it seems you could pretty much manage your whole system with mise. It has dotfiles and bootstrapping scripts after all. But for me, right now, I enjoy the way mise and chezmoi work nicely together. That’s what I’m doing right now, but I might experiment more with mise dotfile management in the future.